From e2dc06ec6695723964cc3c1f1c909469dbc224b5 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Mon, 30 Mar 2026 15:02:25 +0000 Subject: [PATCH 01/38] Bump pygments from 2.19.2 to 2.20.0 Bumps [pygments](https://github.com/pygments/pygments) from 2.19.2 to 2.20.0. - [Release notes](https://github.com/pygments/pygments/releases) - [Changelog](https://github.com/pygments/pygments/blob/master/CHANGES) - [Commits](https://github.com/pygments/pygments/compare/2.19.2...2.20.0) --- updated-dependencies: - dependency-name: pygments dependency-version: 2.20.0 dependency-type: indirect ... Signed-off-by: dependabot[bot] --- poetry.lock | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/poetry.lock b/poetry.lock index 17e0cbc1..96bcd06e 100644 --- a/poetry.lock +++ b/poetry.lock @@ -1147,14 +1147,14 @@ files = [ [[package]] name = "pygments" -version = "2.19.2" +version = "2.20.0" description = "Pygments is a syntax highlighting package written in Python." optional = false -python-versions = ">=3.8" +python-versions = ">=3.9" groups = ["main"] files = [ - {file = "pygments-2.19.2-py3-none-any.whl", hash = "sha256:86540386c03d588bb81d44bc3928634ff26449851e99741617ecb9037ee5ec0b"}, - {file = "pygments-2.19.2.tar.gz", hash = "sha256:636cb2477cec7f8952536970bc533bc43743542f70392ae026374600add5b887"}, + {file = "pygments-2.20.0-py3-none-any.whl", hash = "sha256:81a9e26dd42fd28a23a2d169d86d7ac03b46e2f8b59ed4698fb4785f946d0176"}, + {file = "pygments-2.20.0.tar.gz", hash = "sha256:6757cd03768053ff99f3039c1a36d6c0aa0b263438fcab17520b30a303a82b5f"}, ] [package.extras] From aaa70da9ee6217dd5cb65ff0745c794c8f5c5d6f Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Mon, 6 Apr 2026 05:01:35 +0000 Subject: [PATCH 02/38] Bump click from 8.3.1 to 8.3.2 (#584) Bumps [click](https://github.com/pallets/click) from 8.3.1 to 8.3.2. - [Release notes](https://github.com/pallets/click/releases) - [Changelog](https://github.com/pallets/click/blob/main/CHANGES.rst) - [Commits](https://github.com/pallets/click/compare/8.3.1...8.3.2) --- updated-dependencies: - dependency-name: click dependency-version: 8.3.2 dependency-type: direct:production update-type: version-update:semver-patch ... Signed-off-by: dependabot[bot] Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> --- poetry.lock | 8 ++++---- pyproject.toml | 2 +- 2 files changed, 5 insertions(+), 5 deletions(-) diff --git a/poetry.lock b/poetry.lock index 96bcd06e..804d210e 100644 --- a/poetry.lock +++ b/poetry.lock @@ -308,14 +308,14 @@ files = [ [[package]] name = "click" -version = "8.3.1" +version = "8.3.2" description = "Composable command line interface toolkit" optional = false python-versions = ">=3.10" groups = ["main"] files = [ - {file = "click-8.3.1-py3-none-any.whl", hash = "sha256:981153a64e25f12d547d3426c367a4857371575ee7ad18df2a6183ab0545b2a6"}, - {file = "click-8.3.1.tar.gz", hash = "sha256:12ff4785d337a1bb490bb7e9c2b1ee5da3112e94a8622f26a6c77f5d2fc6842a"}, + {file = "click-8.3.2-py3-none-any.whl", hash = "sha256:1924d2c27c5653561cd2cae4548d1406039cb79b858b747cfea24924bbc1616d"}, + {file = "click-8.3.2.tar.gz", hash = "sha256:14162b8b3b3550a7d479eafa77dfd3c38d9dc8951f6f69c78913a8f9a7540fd5"}, ] [package.dependencies] @@ -1462,4 +1462,4 @@ zstd = ["backports-zstd (>=1.0.0) ; python_version < \"3.14\""] [metadata] lock-version = "2.1" python-versions = "^3.13" -content-hash = "ef50b54454df9eb07c3e2868d7cc66e43eb1abc20e0a782f1584c7ceeafbe6a8" +content-hash = "9faec8902ab6e77719152279d02d39be68d8db8a7ec172b3cdca96dd0f4ac100" diff --git a/pyproject.toml b/pyproject.toml index d4dd5a7b..ea8e2a9b 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -29,7 +29,7 @@ boto3 = "^1.37.38" ansible-core = "^2.19.8" urllib3 = "^2.6.3" cryptography = "46.0.6" -click="^8.1.2" +click="^8.3.2" [poetry.group.dev.dependencies] ansible-lint = "^6.0" From ea7418980e23c36e63b62e5c11f78d8adf8a7683 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Wed, 8 Apr 2026 04:54:16 +0000 Subject: [PATCH 03/38] Bump cryptography from 46.0.6 to 46.0.7 (#585) Bumps [cryptography](https://github.com/pyca/cryptography) from 46.0.6 to 46.0.7. - [Changelog](https://github.com/pyca/cryptography/blob/main/CHANGELOG.rst) - [Commits](https://github.com/pyca/cryptography/compare/46.0.6...46.0.7) --- updated-dependencies: - dependency-name: cryptography dependency-version: 46.0.7 dependency-type: direct:production update-type: version-update:semver-patch ... Signed-off-by: dependabot[bot] Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> --- poetry.lock | 104 ++++++++++++++++++++++++------------------------- pyproject.toml | 2 +- 2 files changed, 53 insertions(+), 53 deletions(-) diff --git a/poetry.lock b/poetry.lock index 804d210e..103f95de 100644 --- a/poetry.lock +++ b/poetry.lock @@ -440,61 +440,61 @@ toml = ["tomli ; python_full_version <= \"3.11.0a6\""] [[package]] name = "cryptography" -version = "46.0.6" +version = "46.0.7" description = "cryptography is a package which provides cryptographic recipes and primitives to Python developers." optional = false python-versions = "!=3.9.0,!=3.9.1,>=3.8" groups = ["main"] files = [ - {file = "cryptography-46.0.6-cp311-abi3-macosx_10_9_universal2.whl", hash = "sha256:64235194bad039a10bb6d2d930ab3323baaec67e2ce36215fd0952fad0930ca8"}, - {file = "cryptography-46.0.6-cp311-abi3-manylinux2014_aarch64.manylinux_2_17_aarch64.whl", hash = "sha256:26031f1e5ca62fcb9d1fcb34b2b60b390d1aacaa15dc8b895a9ed00968b97b30"}, - {file = "cryptography-46.0.6-cp311-abi3-manylinux2014_x86_64.manylinux_2_17_x86_64.whl", hash = "sha256:9a693028b9cbe51b5a1136232ee8f2bc242e4e19d456ded3fa7c86e43c713b4a"}, - {file = "cryptography-46.0.6-cp311-abi3-manylinux_2_28_aarch64.whl", hash = "sha256:67177e8a9f421aa2d3a170c3e56eca4e0128883cf52a071a7cbf53297f18b175"}, - {file = "cryptography-46.0.6-cp311-abi3-manylinux_2_28_ppc64le.whl", hash = "sha256:d9528b535a6c4f8ff37847144b8986a9a143585f0540fbcb1a98115b543aa463"}, - {file = "cryptography-46.0.6-cp311-abi3-manylinux_2_28_x86_64.whl", hash = "sha256:22259338084d6ae497a19bae5d4c66b7ca1387d3264d1c2c0e72d9e9b6a77b97"}, - {file = "cryptography-46.0.6-cp311-abi3-manylinux_2_31_armv7l.whl", hash = "sha256:760997a4b950ff00d418398ad73fbc91aa2894b5c1db7ccb45b4f68b42a63b3c"}, - {file = "cryptography-46.0.6-cp311-abi3-manylinux_2_34_aarch64.whl", hash = "sha256:3dfa6567f2e9e4c5dceb8ccb5a708158a2a871052fa75c8b78cb0977063f1507"}, - {file = "cryptography-46.0.6-cp311-abi3-manylinux_2_34_ppc64le.whl", hash = "sha256:cdcd3edcbc5d55757e5f5f3d330dd00007ae463a7e7aa5bf132d1f22a4b62b19"}, - {file = "cryptography-46.0.6-cp311-abi3-manylinux_2_34_x86_64.whl", hash = "sha256:d4e4aadb7fc1f88687f47ca20bb7227981b03afaae69287029da08096853b738"}, - {file = "cryptography-46.0.6-cp311-abi3-musllinux_1_2_aarch64.whl", hash = "sha256:2b417edbe8877cda9022dde3a008e2deb50be9c407eef034aeeb3a8b11d9db3c"}, - {file = "cryptography-46.0.6-cp311-abi3-musllinux_1_2_x86_64.whl", hash = "sha256:380343e0653b1c9d7e1f55b52aaa2dbb2fdf2730088d48c43ca1c7c0abb7cc2f"}, - {file = "cryptography-46.0.6-cp311-abi3-win32.whl", hash = "sha256:bcb87663e1f7b075e48c3be3ecb5f0b46c8fc50b50a97cf264e7f60242dca3f2"}, - {file = "cryptography-46.0.6-cp311-abi3-win_amd64.whl", hash = "sha256:6739d56300662c468fddb0e5e291f9b4d084bead381667b9e654c7dd81705124"}, - {file = "cryptography-46.0.6-cp314-cp314t-macosx_10_9_universal2.whl", hash = "sha256:2ef9e69886cbb137c2aef9772c2e7138dc581fad4fcbcf13cc181eb5a3ab6275"}, - {file = "cryptography-46.0.6-cp314-cp314t-manylinux2014_aarch64.manylinux_2_17_aarch64.whl", hash = "sha256:7f417f034f91dcec1cb6c5c35b07cdbb2ef262557f701b4ecd803ee8cefed4f4"}, - {file = "cryptography-46.0.6-cp314-cp314t-manylinux2014_x86_64.manylinux_2_17_x86_64.whl", hash = "sha256:d24c13369e856b94892a89ddf70b332e0b70ad4a5c43cf3e9cb71d6d7ffa1f7b"}, - {file = "cryptography-46.0.6-cp314-cp314t-manylinux_2_28_aarch64.whl", hash = "sha256:aad75154a7ac9039936d50cf431719a2f8d4ed3d3c277ac03f3339ded1a5e707"}, - {file = "cryptography-46.0.6-cp314-cp314t-manylinux_2_28_ppc64le.whl", hash = "sha256:3c21d92ed15e9cfc6eb64c1f5a0326db22ca9c2566ca46d845119b45b4400361"}, - {file = "cryptography-46.0.6-cp314-cp314t-manylinux_2_28_x86_64.whl", hash = "sha256:4668298aef7cddeaf5c6ecc244c2302a2b8e40f384255505c22875eebb47888b"}, - {file = "cryptography-46.0.6-cp314-cp314t-manylinux_2_31_armv7l.whl", hash = "sha256:8ce35b77aaf02f3b59c90b2c8a05c73bac12cea5b4e8f3fbece1f5fddea5f0ca"}, - {file = "cryptography-46.0.6-cp314-cp314t-manylinux_2_34_aarch64.whl", hash = "sha256:c89eb37fae9216985d8734c1afd172ba4927f5a05cfd9bf0e4863c6d5465b013"}, - {file = "cryptography-46.0.6-cp314-cp314t-manylinux_2_34_ppc64le.whl", hash = "sha256:ed418c37d095aeddf5336898a132fba01091f0ac5844e3e8018506f014b6d2c4"}, - {file = "cryptography-46.0.6-cp314-cp314t-manylinux_2_34_x86_64.whl", hash = "sha256:69cf0056d6947edc6e6760e5f17afe4bea06b56a9ac8a06de9d2bd6b532d4f3a"}, - {file = "cryptography-46.0.6-cp314-cp314t-musllinux_1_2_aarch64.whl", hash = "sha256:8e7304c4f4e9490e11efe56af6713983460ee0780f16c63f219984dab3af9d2d"}, - {file = "cryptography-46.0.6-cp314-cp314t-musllinux_1_2_x86_64.whl", hash = "sha256:b928a3ca837c77a10e81a814a693f2295200adb3352395fad024559b7be7a736"}, - {file = "cryptography-46.0.6-cp314-cp314t-win32.whl", hash = "sha256:97c8115b27e19e592a05c45d0dd89c57f81f841cc9880e353e0d3bf25b2139ed"}, - {file = "cryptography-46.0.6-cp314-cp314t-win_amd64.whl", hash = "sha256:c797e2517cb7880f8297e2c0f43bb910e91381339336f75d2c1c2cbf811b70b4"}, - {file = "cryptography-46.0.6-cp38-abi3-macosx_10_9_universal2.whl", hash = "sha256:12cae594e9473bca1a7aceb90536060643128bb274fcea0fc459ab90f7d1ae7a"}, - {file = "cryptography-46.0.6-cp38-abi3-manylinux2014_aarch64.manylinux_2_17_aarch64.whl", hash = "sha256:639301950939d844a9e1c4464d7e07f902fe9a7f6b215bb0d4f28584729935d8"}, - {file = "cryptography-46.0.6-cp38-abi3-manylinux2014_x86_64.manylinux_2_17_x86_64.whl", hash = "sha256:ed3775295fb91f70b4027aeba878d79b3e55c0b3e97eaa4de71f8f23a9f2eb77"}, - {file = "cryptography-46.0.6-cp38-abi3-manylinux_2_28_aarch64.whl", hash = "sha256:8927ccfbe967c7df312ade694f987e7e9e22b2425976ddbf28271d7e58845290"}, - {file = "cryptography-46.0.6-cp38-abi3-manylinux_2_28_ppc64le.whl", hash = "sha256:b12c6b1e1651e42ab5de8b1e00dc3b6354fdfd778e7fa60541ddacc27cd21410"}, - {file = "cryptography-46.0.6-cp38-abi3-manylinux_2_28_x86_64.whl", hash = "sha256:063b67749f338ca9c5a0b7fe438a52c25f9526b851e24e6c9310e7195aad3b4d"}, - {file = "cryptography-46.0.6-cp38-abi3-manylinux_2_31_armv7l.whl", hash = "sha256:02fad249cb0e090b574e30b276a3da6a149e04ee2f049725b1f69e7b8351ec70"}, - {file = "cryptography-46.0.6-cp38-abi3-manylinux_2_34_aarch64.whl", hash = "sha256:7e6142674f2a9291463e5e150090b95a8519b2fb6e6aaec8917dd8d094ce750d"}, - {file = "cryptography-46.0.6-cp38-abi3-manylinux_2_34_ppc64le.whl", hash = "sha256:456b3215172aeefb9284550b162801d62f5f264a081049a3e94307fe20792cfa"}, - {file = "cryptography-46.0.6-cp38-abi3-manylinux_2_34_x86_64.whl", hash = "sha256:341359d6c9e68834e204ceaf25936dffeafea3829ab80e9503860dcc4f4dac58"}, - {file = "cryptography-46.0.6-cp38-abi3-musllinux_1_2_aarch64.whl", hash = "sha256:9a9c42a2723999a710445bc0d974e345c32adfd8d2fac6d8a251fa829ad31cfb"}, - {file = "cryptography-46.0.6-cp38-abi3-musllinux_1_2_x86_64.whl", hash = "sha256:6617f67b1606dfd9fe4dbfa354a9508d4a6d37afe30306fe6c101b7ce3274b72"}, - {file = "cryptography-46.0.6-cp38-abi3-win32.whl", hash = "sha256:7f6690b6c55e9c5332c0b59b9c8a3fb232ebf059094c17f9019a51e9827df91c"}, - {file = "cryptography-46.0.6-cp38-abi3-win_amd64.whl", hash = "sha256:79e865c642cfc5c0b3eb12af83c35c5aeff4fa5c672dc28c43721c2c9fdd2f0f"}, - {file = "cryptography-46.0.6-pp311-pypy311_pp73-macosx_11_0_arm64.whl", hash = "sha256:2ea0f37e9a9cf0df2952893ad145fd9627d326a59daec9b0802480fa3bcd2ead"}, - {file = "cryptography-46.0.6-pp311-pypy311_pp73-manylinux_2_28_aarch64.whl", hash = "sha256:a3e84d5ec9ba01f8fd03802b2147ba77f0c8f2617b2aff254cedd551844209c8"}, - {file = "cryptography-46.0.6-pp311-pypy311_pp73-manylinux_2_28_x86_64.whl", hash = "sha256:12f0fa16cc247b13c43d56d7b35287ff1569b5b1f4c5e87e92cc4fcc00cd10c0"}, - {file = "cryptography-46.0.6-pp311-pypy311_pp73-manylinux_2_34_aarch64.whl", hash = "sha256:50575a76e2951fe7dbd1f56d181f8c5ceeeb075e9ff88e7ad997d2f42af06e7b"}, - {file = "cryptography-46.0.6-pp311-pypy311_pp73-manylinux_2_34_x86_64.whl", hash = "sha256:90e5f0a7b3be5f40c3a0a0eafb32c681d8d2c181fc2a1bdabe9b3f611d9f6b1a"}, - {file = "cryptography-46.0.6-pp311-pypy311_pp73-win_amd64.whl", hash = "sha256:6728c49e3b2c180ef26f8e9f0a883a2c585638db64cf265b49c9ba10652d430e"}, - {file = "cryptography-46.0.6.tar.gz", hash = "sha256:27550628a518c5c6c903d84f637fbecf287f6cb9ced3804838a1295dc1fd0759"}, + {file = "cryptography-46.0.7-cp311-abi3-macosx_10_9_universal2.whl", hash = "sha256:ea42cbe97209df307fdc3b155f1b6fa2577c0defa8f1f7d3be7d31d189108ad4"}, + {file = "cryptography-46.0.7-cp311-abi3-manylinux2014_aarch64.manylinux_2_17_aarch64.whl", hash = "sha256:b36a4695e29fe69215d75960b22577197aca3f7a25b9cf9d165dcfe9d80bc325"}, + {file = "cryptography-46.0.7-cp311-abi3-manylinux2014_x86_64.manylinux_2_17_x86_64.whl", hash = "sha256:5ad9ef796328c5e3c4ceed237a183f5d41d21150f972455a9d926593a1dcb308"}, + {file = "cryptography-46.0.7-cp311-abi3-manylinux_2_28_aarch64.whl", hash = "sha256:73510b83623e080a2c35c62c15298096e2a5dc8d51c3b4e1740211839d0dea77"}, + {file = "cryptography-46.0.7-cp311-abi3-manylinux_2_28_ppc64le.whl", hash = "sha256:cbd5fb06b62bd0721e1170273d3f4d5a277044c47ca27ee257025146c34cbdd1"}, + {file = "cryptography-46.0.7-cp311-abi3-manylinux_2_28_x86_64.whl", hash = "sha256:420b1e4109cc95f0e5700eed79908cef9268265c773d3a66f7af1eef53d409ef"}, + {file = "cryptography-46.0.7-cp311-abi3-manylinux_2_31_armv7l.whl", hash = "sha256:24402210aa54baae71d99441d15bb5a1919c195398a87b563df84468160a65de"}, + {file = "cryptography-46.0.7-cp311-abi3-manylinux_2_34_aarch64.whl", hash = "sha256:8a469028a86f12eb7d2fe97162d0634026d92a21f3ae0ac87ed1c4a447886c83"}, + {file = "cryptography-46.0.7-cp311-abi3-manylinux_2_34_ppc64le.whl", hash = "sha256:9694078c5d44c157ef3162e3bf3946510b857df5a3955458381d1c7cfc143ddb"}, + {file = "cryptography-46.0.7-cp311-abi3-manylinux_2_34_x86_64.whl", hash = "sha256:42a1e5f98abb6391717978baf9f90dc28a743b7d9be7f0751a6f56a75d14065b"}, + {file = "cryptography-46.0.7-cp311-abi3-musllinux_1_2_aarch64.whl", hash = "sha256:91bbcb08347344f810cbe49065914fe048949648f6bd5c2519f34619142bbe85"}, + {file = "cryptography-46.0.7-cp311-abi3-musllinux_1_2_x86_64.whl", hash = "sha256:5d1c02a14ceb9148cc7816249f64f623fbfee39e8c03b3650d842ad3f34d637e"}, + {file = "cryptography-46.0.7-cp311-abi3-win32.whl", hash = "sha256:d23c8ca48e44ee015cd0a54aeccdf9f09004eba9fc96f38c911011d9ff1bd457"}, + {file = "cryptography-46.0.7-cp311-abi3-win_amd64.whl", hash = "sha256:397655da831414d165029da9bc483bed2fe0e75dde6a1523ec2fe63f3c46046b"}, + {file = "cryptography-46.0.7-cp314-cp314t-macosx_10_9_universal2.whl", hash = "sha256:d151173275e1728cf7839aaa80c34fe550c04ddb27b34f48c232193df8db5842"}, + {file = "cryptography-46.0.7-cp314-cp314t-manylinux2014_aarch64.manylinux_2_17_aarch64.whl", hash = "sha256:db0f493b9181c7820c8134437eb8b0b4792085d37dbb24da050476ccb664e59c"}, + {file = "cryptography-46.0.7-cp314-cp314t-manylinux2014_x86_64.manylinux_2_17_x86_64.whl", hash = "sha256:ebd6daf519b9f189f85c479427bbd6e9c9037862cf8fe89ee35503bd209ed902"}, + {file = "cryptography-46.0.7-cp314-cp314t-manylinux_2_28_aarch64.whl", hash = "sha256:b7b412817be92117ec5ed95f880defe9cf18a832e8cafacf0a22337dc1981b4d"}, + {file = "cryptography-46.0.7-cp314-cp314t-manylinux_2_28_ppc64le.whl", hash = "sha256:fbfd0e5f273877695cb93baf14b185f4878128b250cc9f8e617ea0c025dfb022"}, + {file = "cryptography-46.0.7-cp314-cp314t-manylinux_2_28_x86_64.whl", hash = "sha256:ffca7aa1d00cf7d6469b988c581598f2259e46215e0140af408966a24cf086ce"}, + {file = "cryptography-46.0.7-cp314-cp314t-manylinux_2_31_armv7l.whl", hash = "sha256:60627cf07e0d9274338521205899337c5d18249db56865f943cbe753aa96f40f"}, + {file = "cryptography-46.0.7-cp314-cp314t-manylinux_2_34_aarch64.whl", hash = "sha256:80406c3065e2c55d7f49a9550fe0c49b3f12e5bfff5dedb727e319e1afb9bf99"}, + {file = "cryptography-46.0.7-cp314-cp314t-manylinux_2_34_ppc64le.whl", hash = "sha256:c5b1ccd1239f48b7151a65bc6dd54bcfcc15e028c8ac126d3fada09db0e07ef1"}, + {file = "cryptography-46.0.7-cp314-cp314t-manylinux_2_34_x86_64.whl", hash = "sha256:d5f7520159cd9c2154eb61eb67548ca05c5774d39e9c2c4339fd793fe7d097b2"}, + {file = "cryptography-46.0.7-cp314-cp314t-musllinux_1_2_aarch64.whl", hash = "sha256:fcd8eac50d9138c1d7fc53a653ba60a2bee81a505f9f8850b6b2888555a45d0e"}, + {file = "cryptography-46.0.7-cp314-cp314t-musllinux_1_2_x86_64.whl", hash = "sha256:65814c60f8cc400c63131584e3e1fad01235edba2614b61fbfbfa954082db0ee"}, + {file = "cryptography-46.0.7-cp314-cp314t-win32.whl", hash = "sha256:fdd1736fed309b4300346f88f74cd120c27c56852c3838cab416e7a166f67298"}, + {file = "cryptography-46.0.7-cp314-cp314t-win_amd64.whl", hash = "sha256:e06acf3c99be55aa3b516397fe42f5855597f430add9c17fa46bf2e0fb34c9bb"}, + {file = "cryptography-46.0.7-cp38-abi3-macosx_10_9_universal2.whl", hash = "sha256:462ad5cb1c148a22b2e3bcc5ad52504dff325d17daf5df8d88c17dda1f75f2a4"}, + {file = "cryptography-46.0.7-cp38-abi3-manylinux2014_aarch64.manylinux_2_17_aarch64.whl", hash = "sha256:84d4cced91f0f159a7ddacad249cc077e63195c36aac40b4150e7a57e84fffe7"}, + {file = "cryptography-46.0.7-cp38-abi3-manylinux2014_x86_64.manylinux_2_17_x86_64.whl", hash = "sha256:128c5edfe5e5938b86b03941e94fac9ee793a94452ad1365c9fc3f4f62216832"}, + {file = "cryptography-46.0.7-cp38-abi3-manylinux_2_28_aarch64.whl", hash = "sha256:5e51be372b26ef4ba3de3c167cd3d1022934bc838ae9eaad7e644986d2a3d163"}, + {file = "cryptography-46.0.7-cp38-abi3-manylinux_2_28_ppc64le.whl", hash = "sha256:cdf1a610ef82abb396451862739e3fc93b071c844399e15b90726ef7470eeaf2"}, + {file = "cryptography-46.0.7-cp38-abi3-manylinux_2_28_x86_64.whl", hash = "sha256:1d25aee46d0c6f1a501adcddb2d2fee4b979381346a78558ed13e50aa8a59067"}, + {file = "cryptography-46.0.7-cp38-abi3-manylinux_2_31_armv7l.whl", hash = "sha256:cdfbe22376065ffcf8be74dc9a909f032df19bc58a699456a21712d6e5eabfd0"}, + {file = "cryptography-46.0.7-cp38-abi3-manylinux_2_34_aarch64.whl", hash = "sha256:abad9dac36cbf55de6eb49badd4016806b3165d396f64925bf2999bcb67837ba"}, + {file = "cryptography-46.0.7-cp38-abi3-manylinux_2_34_ppc64le.whl", hash = "sha256:935ce7e3cfdb53e3536119a542b839bb94ec1ad081013e9ab9b7cfd478b05006"}, + {file = "cryptography-46.0.7-cp38-abi3-manylinux_2_34_x86_64.whl", hash = "sha256:35719dc79d4730d30f1c2b6474bd6acda36ae2dfae1e3c16f2051f215df33ce0"}, + {file = "cryptography-46.0.7-cp38-abi3-musllinux_1_2_aarch64.whl", hash = "sha256:7bbc6ccf49d05ac8f7d7b5e2e2c33830d4fe2061def88210a126d130d7f71a85"}, + {file = "cryptography-46.0.7-cp38-abi3-musllinux_1_2_x86_64.whl", hash = "sha256:a1529d614f44b863a7b480c6d000fe93b59acee9c82ffa027cfadc77521a9f5e"}, + {file = "cryptography-46.0.7-cp38-abi3-win32.whl", hash = "sha256:f247c8c1a1fb45e12586afbb436ef21ff1e80670b2861a90353d9b025583d246"}, + {file = "cryptography-46.0.7-cp38-abi3-win_amd64.whl", hash = "sha256:506c4ff91eff4f82bdac7633318a526b1d1309fc07ca76a3ad182cb5b686d6d3"}, + {file = "cryptography-46.0.7-pp311-pypy311_pp73-macosx_11_0_arm64.whl", hash = "sha256:fc9ab8856ae6cf7c9358430e49b368f3108f050031442eaeb6b9d87e4dcf4e4f"}, + {file = "cryptography-46.0.7-pp311-pypy311_pp73-manylinux_2_28_aarch64.whl", hash = "sha256:d3b99c535a9de0adced13d159c5a9cf65c325601aa30f4be08afd680643e9c15"}, + {file = "cryptography-46.0.7-pp311-pypy311_pp73-manylinux_2_28_x86_64.whl", hash = "sha256:d02c738dacda7dc2a74d1b2b3177042009d5cab7c7079db74afc19e56ca1b455"}, + {file = "cryptography-46.0.7-pp311-pypy311_pp73-manylinux_2_34_aarch64.whl", hash = "sha256:04959522f938493042d595a736e7dbdff6eb6cc2339c11465b3ff89343b65f65"}, + {file = "cryptography-46.0.7-pp311-pypy311_pp73-manylinux_2_34_x86_64.whl", hash = "sha256:3986ac1dee6def53797289999eabe84798ad7817f3e97779b5061a95b0ee4968"}, + {file = "cryptography-46.0.7-pp311-pypy311_pp73-win_amd64.whl", hash = "sha256:258514877e15963bd43b558917bc9f54cf7cf866c38aa576ebf47a77ddbc43a4"}, + {file = "cryptography-46.0.7.tar.gz", hash = "sha256:e4cfd68c5f3e0bfdad0d38e023239b96a2fe84146481852dffbcca442c245aa5"}, ] [package.dependencies] @@ -507,7 +507,7 @@ nox = ["nox[uv] (>=2024.4.15)"] pep8test = ["check-sdist", "click (>=8.0.1)", "mypy (>=1.14)", "ruff (>=0.11.11)"] sdist = ["build (>=1.0.0)"] ssh = ["bcrypt (>=3.1.5)"] -test = ["certifi (>=2024)", "cryptography-vectors (==46.0.6)", "pretend (>=0.7)", "pytest (>=7.4.0)", "pytest-benchmark (>=4.0)", "pytest-cov (>=2.10.1)", "pytest-xdist (>=3.5.0)"] +test = ["certifi (>=2024)", "cryptography-vectors (==46.0.7)", "pretend (>=0.7)", "pytest (>=7.4.0)", "pytest-benchmark (>=4.0)", "pytest-cov (>=2.10.1)", "pytest-xdist (>=3.5.0)"] test-randomorder = ["pytest-randomly"] [[package]] @@ -1462,4 +1462,4 @@ zstd = ["backports-zstd (>=1.0.0) ; python_version < \"3.14\""] [metadata] lock-version = "2.1" python-versions = "^3.13" -content-hash = "9faec8902ab6e77719152279d02d39be68d8db8a7ec172b3cdca96dd0f4ac100" +content-hash = "1701bc7dfae244547e0835156e41673ccde6731fb5ad4ec67cc3cf39029ca1a9" diff --git a/pyproject.toml b/pyproject.toml index ea8e2a9b..d0c9528f 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -28,7 +28,7 @@ lxml = "^5.0" boto3 = "^1.37.38" ansible-core = "^2.19.8" urllib3 = "^2.6.3" -cryptography = "46.0.6" +cryptography = "46.0.7" click="^8.3.2" [poetry.group.dev.dependencies] From 034439d6659198f73b1f2610114933c480d95b13 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Thu, 9 Apr 2026 04:53:47 +0000 Subject: [PATCH 04/38] Bump pytest from 9.0.2 to 9.0.3 (#586) Bumps [pytest](https://github.com/pytest-dev/pytest) from 9.0.2 to 9.0.3. - [Release notes](https://github.com/pytest-dev/pytest/releases) - [Changelog](https://github.com/pytest-dev/pytest/blob/main/CHANGELOG.rst) - [Commits](https://github.com/pytest-dev/pytest/compare/9.0.2...9.0.3) --- updated-dependencies: - dependency-name: pytest dependency-version: 9.0.3 dependency-type: direct:production update-type: version-update:semver-patch ... Signed-off-by: dependabot[bot] Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> --- poetry.lock | 8 ++++---- pyproject.toml | 2 +- 2 files changed, 5 insertions(+), 5 deletions(-) diff --git a/poetry.lock b/poetry.lock index 103f95de..c81c808b 100644 --- a/poetry.lock +++ b/poetry.lock @@ -1162,14 +1162,14 @@ windows-terminal = ["colorama (>=0.4.6)"] [[package]] name = "pytest" -version = "9.0.2" +version = "9.0.3" description = "pytest: simple powerful testing with Python" optional = false python-versions = ">=3.10" groups = ["main"] files = [ - {file = "pytest-9.0.2-py3-none-any.whl", hash = "sha256:711ffd45bf766d5264d487b917733b453d917afd2b0ad65223959f59089f875b"}, - {file = "pytest-9.0.2.tar.gz", hash = "sha256:75186651a92bd89611d1d9fc20f0b4345fd827c41ccd5c299a868a05d70edf11"}, + {file = "pytest-9.0.3-py3-none-any.whl", hash = "sha256:2c5efc453d45394fdd706ade797c0a81091eccd1d6e4bccfcd476e2b8e0ab5d9"}, + {file = "pytest-9.0.3.tar.gz", hash = "sha256:b86ada508af81d19edeb213c681b1d48246c1a91d304c6c81a427674c17eb91c"}, ] [package.dependencies] @@ -1462,4 +1462,4 @@ zstd = ["backports-zstd (>=1.0.0) ; python_version < \"3.14\""] [metadata] lock-version = "2.1" python-versions = "^3.13" -content-hash = "1701bc7dfae244547e0835156e41673ccde6731fb5ad4ec67cc3cf39029ca1a9" +content-hash = "8963caf3dd6cb8844ff50e010e94a986dbcc5a6d1a2fad9ecb23864c6f14f203" diff --git a/pyproject.toml b/pyproject.toml index d0c9528f..f2652cc9 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -13,7 +13,7 @@ jinja2 = "^3.1.6" requests = "^2.32.5" flake8 = "^7.3.0" mypy = "^1.18.2" -pytest = "^9.0.2" +pytest = "^9.0.3" pytest-cov = "^7.0.0" ansible = "^12.2.0" pyyaml = "^6.0.3" From 305efc0b4a2ee11b301f9897522f3b54084b42a4 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Fri, 17 Apr 2026 04:55:40 +0000 Subject: [PATCH 05/38] Bump actions/upload-artifact from 7.0.0 to 7.0.1 (#588) Bumps [actions/upload-artifact](https://github.com/actions/upload-artifact) from 7.0.0 to 7.0.1. - [Release notes](https://github.com/actions/upload-artifact/releases) - [Commits](https://github.com/actions/upload-artifact/compare/bbbca2ddaa5d8feaa63e36b76fdaad77386f024f...043fb46d1a93c77aae656e7c1c64a875d1fc6a0a) --- updated-dependencies: - dependency-name: actions/upload-artifact dependency-version: 7.0.1 dependency-type: direct:production update-type: version-update:semver-patch ... Signed-off-by: dependabot[bot] Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> --- .github/workflows/sbom.yml | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/.github/workflows/sbom.yml b/.github/workflows/sbom.yml index 341a437e..7f8d47f6 100644 --- a/.github/workflows/sbom.yml +++ b/.github/workflows/sbom.yml @@ -69,7 +69,7 @@ jobs: python .github/scripts/sbom_json_to_csv.py sbom.json SBOM_${REPO_NAME}.csv - name: Upload SBOM CSV as artifact - uses: actions/upload-artifact@bbbca2ddaa5d8feaa63e36b76fdaad77386f024f # v7.0.0 + uses: actions/upload-artifact@043fb46d1a93c77aae656e7c1c64a875d1fc6a0a # v7.0.1 with: name: sbom-csv path: SBOM_${{ github.event.repository.name }}.csv @@ -89,7 +89,7 @@ jobs: python .github/scripts/grype_json_to_csv.py grype-report.json grype-report-${REPO_NAME}.csv - name: Upload Vulnerability Report - uses: actions/upload-artifact@bbbca2ddaa5d8feaa63e36b76fdaad77386f024f # v7.0.0 + uses: actions/upload-artifact@043fb46d1a93c77aae656e7c1c64a875d1fc6a0a # v7.0.1 with: name: grype-report path: grype-report-${{ github.event.repository.name }}.csv @@ -101,7 +101,7 @@ jobs: python .github/scripts/sbom_packages_to_csv.py sbom.json $REPO_NAME - name: Upload Package Inventory CSV - uses: actions/upload-artifact@bbbca2ddaa5d8feaa63e36b76fdaad77386f024f # v7.0.0 + uses: actions/upload-artifact@043fb46d1a93c77aae656e7c1c64a875d1fc6a0a # v7.0.1 with: name: sbom-packages path: sbom-packages-${{ github.event.repository.name }}.csv From 3c1be50a1b5819a4c48b34f1b21349772b8f7f83 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Mon, 20 Apr 2026 05:12:14 +0000 Subject: [PATCH 06/38] Bump actions/cache from 5.0.4 to 5.0.5 (#589) Bumps [actions/cache](https://github.com/actions/cache) from 5.0.4 to 5.0.5. - [Release notes](https://github.com/actions/cache/releases) - [Changelog](https://github.com/actions/cache/blob/main/RELEASES.md) - [Commits](https://github.com/actions/cache/compare/668228422ae6a00e4ad889ee87cd7109ec5666a7...27d5ce7f107fe9357f9df03efb73ab90386fccae) --- updated-dependencies: - dependency-name: actions/cache dependency-version: 5.0.5 dependency-type: direct:production update-type: version-update:semver-patch ... Signed-off-by: dependabot[bot] Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> --- .github/workflows/publish.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/workflows/publish.yml b/.github/workflows/publish.yml index 58ef2d0c..25cd254d 100644 --- a/.github/workflows/publish.yml +++ b/.github/workflows/publish.yml @@ -21,7 +21,7 @@ jobs: run: pip install poetry - name: Cache poetry packages - uses: actions/cache@668228422ae6a00e4ad889ee87cd7109ec5666a7 # v5.0.4 + uses: actions/cache@27d5ce7f107fe9357f9df03efb73ab90386fccae # v5.0.5 id: cache with: path: ~/.venv From 237cee23b9ae4bed3300a9da11adfadf8afe9115 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Tue, 21 Apr 2026 04:54:07 +0000 Subject: [PATCH 07/38] Bump ansible-core from 2.19.8 to 2.19.9 (#590) Bumps [ansible-core](https://github.com/ansible/ansible) from 2.19.8 to 2.19.9. - [Release notes](https://github.com/ansible/ansible/releases) - [Commits](https://github.com/ansible/ansible/compare/v2.19.8...v2.19.9) --- updated-dependencies: - dependency-name: ansible-core dependency-version: 2.19.9 dependency-type: direct:production update-type: version-update:semver-patch ... Signed-off-by: dependabot[bot] Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> --- poetry.lock | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/poetry.lock b/poetry.lock index c81c808b..6e8511ee 100644 --- a/poetry.lock +++ b/poetry.lock @@ -17,14 +17,14 @@ ansible-core = ">=2.19.4,<2.20.0" [[package]] name = "ansible-core" -version = "2.19.8" +version = "2.19.9" description = "Radically simple IT automation" optional = false python-versions = ">=3.11" groups = ["main"] files = [ - {file = "ansible_core-2.19.8-py3-none-any.whl", hash = "sha256:93be79b08563dafe711e07d6e468b780acdbbaa1da4d9c8e7efae95f45167975"}, - {file = "ansible_core-2.19.8.tar.gz", hash = "sha256:676e70925b775d2c8526b2bcea59c4b2195221bd6e4e3c0537e89387e08608bd"}, + {file = "ansible_core-2.19.9-py3-none-any.whl", hash = "sha256:18de80e3ea5a89d2ea84e4d4a3c6d7c121ea62fc3af2877ba083784e3d90b419"}, + {file = "ansible_core-2.19.9.tar.gz", hash = "sha256:74107de13d188ff579fb215bc3eb875c9198803215d6378ed588c7f35aba12f5"}, ] [package.dependencies] From baa0d30d0a776dfbdd2139c57beca79a62809269 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Wed, 22 Apr 2026 04:53:43 +0000 Subject: [PATCH 08/38] Bump gitpython from 3.1.46 to 3.1.47 (#592) Bumps [gitpython](https://github.com/gitpython-developers/GitPython) from 3.1.46 to 3.1.47. - [Release notes](https://github.com/gitpython-developers/GitPython/releases) - [Changelog](https://github.com/gitpython-developers/GitPython/blob/main/CHANGES) - [Commits](https://github.com/gitpython-developers/GitPython/compare/3.1.46...3.1.47) --- updated-dependencies: - dependency-name: gitpython dependency-version: 3.1.47 dependency-type: direct:production update-type: version-update:semver-patch ... Signed-off-by: dependabot[bot] Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> --- poetry.lock | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/poetry.lock b/poetry.lock index 6e8511ee..862ee1f3 100644 --- a/poetry.lock +++ b/poetry.lock @@ -606,21 +606,21 @@ smmap = ">=3.0.1,<6" [[package]] name = "gitpython" -version = "3.1.46" +version = "3.1.47" description = "GitPython is a Python library used to interact with Git repositories" optional = false python-versions = ">=3.7" groups = ["main"] files = [ - {file = "gitpython-3.1.46-py3-none-any.whl", hash = "sha256:79812ed143d9d25b6d176a10bb511de0f9c67b1fa641d82097b0ab90398a2058"}, - {file = "gitpython-3.1.46.tar.gz", hash = "sha256:400124c7d0ef4ea03f7310ac2fbf7151e09ff97f2a3288d64a440c584a29c37f"}, + {file = "gitpython-3.1.47-py3-none-any.whl", hash = "sha256:489f590edfd6d20571b2c0e72c6a6ac6915ee8b8cd04572330e3842207a78905"}, + {file = "gitpython-3.1.47.tar.gz", hash = "sha256:dba27f922bd2b42cb54c87a8ab3cb6beb6bf07f3d564e21ac848913a05a8a3cd"}, ] [package.dependencies] gitdb = ">=4.0.1,<5" [package.extras] -doc = ["sphinx (>=7.1.2,<7.2)", "sphinx-autodoc-typehints", "sphinx_rtd_theme"] +doc = ["sphinx (>=7.4.7,<8)", "sphinx-autodoc-typehints", "sphinx_rtd_theme"] test = ["coverage[toml]", "ddt (>=1.1.1,!=1.4.3)", "mock ; python_version < \"3.8\"", "mypy (==1.18.2) ; python_version >= \"3.9\"", "pre-commit", "pytest (>=7.3.1)", "pytest-cov", "pytest-instafail", "pytest-mock", "pytest-sugar", "typing-extensions ; python_version < \"3.11\""] [[package]] From a452e83c6a45d7135087c8a8e4c4160fe9f32194 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Thu, 23 Apr 2026 04:54:56 +0000 Subject: [PATCH 09/38] Bump click from 8.3.2 to 8.3.3 (#594) Bumps [click](https://github.com/pallets/click) from 8.3.2 to 8.3.3. - [Release notes](https://github.com/pallets/click/releases) - [Changelog](https://github.com/pallets/click/blob/8.3.3/CHANGES.rst) - [Commits](https://github.com/pallets/click/compare/8.3.2...8.3.3) --- updated-dependencies: - dependency-name: click dependency-version: 8.3.3 dependency-type: direct:production update-type: version-update:semver-patch ... Signed-off-by: dependabot[bot] Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> --- poetry.lock | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/poetry.lock b/poetry.lock index 862ee1f3..ae08373d 100644 --- a/poetry.lock +++ b/poetry.lock @@ -308,14 +308,14 @@ files = [ [[package]] name = "click" -version = "8.3.2" +version = "8.3.3" description = "Composable command line interface toolkit" optional = false python-versions = ">=3.10" groups = ["main"] files = [ - {file = "click-8.3.2-py3-none-any.whl", hash = "sha256:1924d2c27c5653561cd2cae4548d1406039cb79b858b747cfea24924bbc1616d"}, - {file = "click-8.3.2.tar.gz", hash = "sha256:14162b8b3b3550a7d479eafa77dfd3c38d9dc8951f6f69c78913a8f9a7540fd5"}, + {file = "click-8.3.3-py3-none-any.whl", hash = "sha256:a2bf429bb3033c89fa4936ffb35d5cb471e3719e1f3c8a7c3fff0b8314305613"}, + {file = "click-8.3.3.tar.gz", hash = "sha256:398329ad4837b2ff7cbe1dd166a4c0f8900c3ca3a218de04466f38f6497f18a2"}, ] [package.dependencies] From 28602168bc6f4346821d3a623cd9d555bed7977f Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Thu, 23 Apr 2026 04:56:31 +0000 Subject: [PATCH 10/38] Bump lxml from 5.4.0 to 6.1.0 Bumps [lxml](https://github.com/lxml/lxml) from 5.4.0 to 6.1.0. - [Release notes](https://github.com/lxml/lxml/releases) - [Changelog](https://github.com/lxml/lxml/blob/master/CHANGES.txt) - [Commits](https://github.com/lxml/lxml/compare/lxml-5.4.0...lxml-6.1.0) --- updated-dependencies: - dependency-name: lxml dependency-version: 6.1.0 dependency-type: direct:production ... Signed-off-by: dependabot[bot] --- poetry.lock | 273 +++++++++++++++++++++++++------------------------ pyproject.toml | 2 +- 2 files changed, 138 insertions(+), 137 deletions(-) diff --git a/poetry.lock b/poetry.lock index ae08373d..34b82a90 100644 --- a/poetry.lock +++ b/poetry.lock @@ -682,144 +682,146 @@ files = [ [[package]] name = "lxml" -version = "5.4.0" +version = "6.1.0" description = "Powerful and Pythonic XML processing library combining libxml2/libxslt with the ElementTree API." optional = false -python-versions = ">=3.6" +python-versions = ">=3.8" groups = ["main"] files = [ - {file = "lxml-5.4.0-cp310-cp310-macosx_10_9_universal2.whl", hash = "sha256:e7bc6df34d42322c5289e37e9971d6ed114e3776b45fa879f734bded9d1fea9c"}, - {file = "lxml-5.4.0-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:6854f8bd8a1536f8a1d9a3655e6354faa6406621cf857dc27b681b69860645c7"}, - {file = "lxml-5.4.0-cp310-cp310-manylinux_2_12_i686.manylinux2010_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:696ea9e87442467819ac22394ca36cb3d01848dad1be6fac3fb612d3bd5a12cf"}, - {file = "lxml-5.4.0-cp310-cp310-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:6ef80aeac414f33c24b3815ecd560cee272786c3adfa5f31316d8b349bfade28"}, - {file = "lxml-5.4.0-cp310-cp310-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:3b9c2754cef6963f3408ab381ea55f47dabc6f78f4b8ebb0f0b25cf1ac1f7609"}, - {file = "lxml-5.4.0-cp310-cp310-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:7a62cc23d754bb449d63ff35334acc9f5c02e6dae830d78dab4dd12b78a524f4"}, - {file = "lxml-5.4.0-cp310-cp310-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:8f82125bc7203c5ae8633a7d5d20bcfdff0ba33e436e4ab0abc026a53a8960b7"}, - {file = "lxml-5.4.0-cp310-cp310-manylinux_2_28_aarch64.whl", hash = "sha256:b67319b4aef1a6c56576ff544b67a2a6fbd7eaee485b241cabf53115e8908b8f"}, - {file = "lxml-5.4.0-cp310-cp310-manylinux_2_28_ppc64le.whl", hash = "sha256:a8ef956fce64c8551221f395ba21d0724fed6b9b6242ca4f2f7beb4ce2f41997"}, - {file = "lxml-5.4.0-cp310-cp310-manylinux_2_28_s390x.whl", hash = "sha256:0a01ce7d8479dce84fc03324e3b0c9c90b1ece9a9bb6a1b6c9025e7e4520e78c"}, - {file = "lxml-5.4.0-cp310-cp310-manylinux_2_28_x86_64.whl", hash = "sha256:91505d3ddebf268bb1588eb0f63821f738d20e1e7f05d3c647a5ca900288760b"}, - {file = "lxml-5.4.0-cp310-cp310-musllinux_1_2_aarch64.whl", hash = "sha256:a3bcdde35d82ff385f4ede021df801b5c4a5bcdfb61ea87caabcebfc4945dc1b"}, - {file = "lxml-5.4.0-cp310-cp310-musllinux_1_2_ppc64le.whl", hash = "sha256:aea7c06667b987787c7d1f5e1dfcd70419b711cdb47d6b4bb4ad4b76777a0563"}, - {file = "lxml-5.4.0-cp310-cp310-musllinux_1_2_s390x.whl", hash = "sha256:a7fb111eef4d05909b82152721a59c1b14d0f365e2be4c742a473c5d7372f4f5"}, - {file = "lxml-5.4.0-cp310-cp310-musllinux_1_2_x86_64.whl", hash = "sha256:43d549b876ce64aa18b2328faff70f5877f8c6dede415f80a2f799d31644d776"}, - {file = "lxml-5.4.0-cp310-cp310-win32.whl", hash = "sha256:75133890e40d229d6c5837b0312abbe5bac1c342452cf0e12523477cd3aa21e7"}, - {file = "lxml-5.4.0-cp310-cp310-win_amd64.whl", hash = "sha256:de5b4e1088523e2b6f730d0509a9a813355b7f5659d70eb4f319c76beea2e250"}, - {file = "lxml-5.4.0-cp311-cp311-macosx_10_9_universal2.whl", hash = "sha256:98a3912194c079ef37e716ed228ae0dcb960992100461b704aea4e93af6b0bb9"}, - {file = "lxml-5.4.0-cp311-cp311-macosx_10_9_x86_64.whl", hash = "sha256:0ea0252b51d296a75f6118ed0d8696888e7403408ad42345d7dfd0d1e93309a7"}, - {file = "lxml-5.4.0-cp311-cp311-manylinux_2_12_i686.manylinux2010_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:b92b69441d1bd39f4940f9eadfa417a25862242ca2c396b406f9272ef09cdcaa"}, - {file = "lxml-5.4.0-cp311-cp311-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:20e16c08254b9b6466526bc1828d9370ee6c0d60a4b64836bc3ac2917d1e16df"}, - {file = "lxml-5.4.0-cp311-cp311-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:7605c1c32c3d6e8c990dd28a0970a3cbbf1429d5b92279e37fda05fb0c92190e"}, - {file = "lxml-5.4.0-cp311-cp311-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:ecf4c4b83f1ab3d5a7ace10bafcb6f11df6156857a3c418244cef41ca9fa3e44"}, - {file = "lxml-5.4.0-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:0cef4feae82709eed352cd7e97ae062ef6ae9c7b5dbe3663f104cd2c0e8d94ba"}, - {file = "lxml-5.4.0-cp311-cp311-manylinux_2_28_aarch64.whl", hash = "sha256:df53330a3bff250f10472ce96a9af28628ff1f4efc51ccba351a8820bca2a8ba"}, - {file = "lxml-5.4.0-cp311-cp311-manylinux_2_28_ppc64le.whl", hash = "sha256:aefe1a7cb852fa61150fcb21a8c8fcea7b58c4cb11fbe59c97a0a4b31cae3c8c"}, - {file = "lxml-5.4.0-cp311-cp311-manylinux_2_28_s390x.whl", hash = "sha256:ef5a7178fcc73b7d8c07229e89f8eb45b2908a9238eb90dcfc46571ccf0383b8"}, - {file = "lxml-5.4.0-cp311-cp311-manylinux_2_28_x86_64.whl", hash = "sha256:d2ed1b3cb9ff1c10e6e8b00941bb2e5bb568b307bfc6b17dffbbe8be5eecba86"}, - {file = "lxml-5.4.0-cp311-cp311-musllinux_1_2_aarch64.whl", hash = "sha256:72ac9762a9f8ce74c9eed4a4e74306f2f18613a6b71fa065495a67ac227b3056"}, - {file = "lxml-5.4.0-cp311-cp311-musllinux_1_2_ppc64le.whl", hash = "sha256:f5cb182f6396706dc6cc1896dd02b1c889d644c081b0cdec38747573db88a7d7"}, - {file = "lxml-5.4.0-cp311-cp311-musllinux_1_2_s390x.whl", hash = "sha256:3a3178b4873df8ef9457a4875703488eb1622632a9cee6d76464b60e90adbfcd"}, - {file = "lxml-5.4.0-cp311-cp311-musllinux_1_2_x86_64.whl", hash = "sha256:e094ec83694b59d263802ed03a8384594fcce477ce484b0cbcd0008a211ca751"}, - {file = "lxml-5.4.0-cp311-cp311-win32.whl", hash = "sha256:4329422de653cdb2b72afa39b0aa04252fca9071550044904b2e7036d9d97fe4"}, - {file = "lxml-5.4.0-cp311-cp311-win_amd64.whl", hash = "sha256:fd3be6481ef54b8cfd0e1e953323b7aa9d9789b94842d0e5b142ef4bb7999539"}, - {file = "lxml-5.4.0-cp312-cp312-macosx_10_9_universal2.whl", hash = "sha256:b5aff6f3e818e6bdbbb38e5967520f174b18f539c2b9de867b1e7fde6f8d95a4"}, - {file = "lxml-5.4.0-cp312-cp312-macosx_10_9_x86_64.whl", hash = "sha256:942a5d73f739ad7c452bf739a62a0f83e2578afd6b8e5406308731f4ce78b16d"}, - {file = "lxml-5.4.0-cp312-cp312-manylinux_2_12_i686.manylinux2010_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:460508a4b07364d6abf53acaa0a90b6d370fafde5693ef37602566613a9b0779"}, - {file = "lxml-5.4.0-cp312-cp312-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:529024ab3a505fed78fe3cc5ddc079464e709f6c892733e3f5842007cec8ac6e"}, - {file = "lxml-5.4.0-cp312-cp312-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:7ca56ebc2c474e8f3d5761debfd9283b8b18c76c4fc0967b74aeafba1f5647f9"}, - {file = "lxml-5.4.0-cp312-cp312-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:a81e1196f0a5b4167a8dafe3a66aa67c4addac1b22dc47947abd5d5c7a3f24b5"}, - {file = "lxml-5.4.0-cp312-cp312-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:00b8686694423ddae324cf614e1b9659c2edb754de617703c3d29ff568448df5"}, - {file = "lxml-5.4.0-cp312-cp312-manylinux_2_28_aarch64.whl", hash = "sha256:c5681160758d3f6ac5b4fea370495c48aac0989d6a0f01bb9a72ad8ef5ab75c4"}, - {file = "lxml-5.4.0-cp312-cp312-manylinux_2_28_ppc64le.whl", hash = "sha256:2dc191e60425ad70e75a68c9fd90ab284df64d9cd410ba8d2b641c0c45bc006e"}, - {file = "lxml-5.4.0-cp312-cp312-manylinux_2_28_s390x.whl", hash = "sha256:67f779374c6b9753ae0a0195a892a1c234ce8416e4448fe1e9f34746482070a7"}, - {file = "lxml-5.4.0-cp312-cp312-manylinux_2_28_x86_64.whl", hash = "sha256:79d5bfa9c1b455336f52343130b2067164040604e41f6dc4d8313867ed540079"}, - {file = "lxml-5.4.0-cp312-cp312-musllinux_1_2_aarch64.whl", hash = "sha256:3d3c30ba1c9b48c68489dc1829a6eede9873f52edca1dda900066542528d6b20"}, - {file = "lxml-5.4.0-cp312-cp312-musllinux_1_2_ppc64le.whl", hash = "sha256:1af80c6316ae68aded77e91cd9d80648f7dd40406cef73df841aa3c36f6907c8"}, - {file = "lxml-5.4.0-cp312-cp312-musllinux_1_2_s390x.whl", hash = "sha256:4d885698f5019abe0de3d352caf9466d5de2baded00a06ef3f1216c1a58ae78f"}, - {file = "lxml-5.4.0-cp312-cp312-musllinux_1_2_x86_64.whl", hash = "sha256:aea53d51859b6c64e7c51d522c03cc2c48b9b5d6172126854cc7f01aa11f52bc"}, - {file = "lxml-5.4.0-cp312-cp312-win32.whl", hash = "sha256:d90b729fd2732df28130c064aac9bb8aff14ba20baa4aee7bd0795ff1187545f"}, - {file = "lxml-5.4.0-cp312-cp312-win_amd64.whl", hash = "sha256:1dc4ca99e89c335a7ed47d38964abcb36c5910790f9bd106f2a8fa2ee0b909d2"}, - {file = "lxml-5.4.0-cp313-cp313-macosx_10_13_universal2.whl", hash = "sha256:773e27b62920199c6197130632c18fb7ead3257fce1ffb7d286912e56ddb79e0"}, - {file = "lxml-5.4.0-cp313-cp313-macosx_10_13_x86_64.whl", hash = "sha256:ce9c671845de9699904b1e9df95acfe8dfc183f2310f163cdaa91a3535af95de"}, - {file = "lxml-5.4.0-cp313-cp313-manylinux_2_12_i686.manylinux2010_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:9454b8d8200ec99a224df8854786262b1bd6461f4280064c807303c642c05e76"}, - {file = "lxml-5.4.0-cp313-cp313-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:cccd007d5c95279e529c146d095f1d39ac05139de26c098166c4beb9374b0f4d"}, - {file = "lxml-5.4.0-cp313-cp313-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:0fce1294a0497edb034cb416ad3e77ecc89b313cff7adbee5334e4dc0d11f422"}, - {file = "lxml-5.4.0-cp313-cp313-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:24974f774f3a78ac12b95e3a20ef0931795ff04dbb16db81a90c37f589819551"}, - {file = "lxml-5.4.0-cp313-cp313-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:497cab4d8254c2a90bf988f162ace2ddbfdd806fce3bda3f581b9d24c852e03c"}, - {file = "lxml-5.4.0-cp313-cp313-manylinux_2_28_aarch64.whl", hash = "sha256:e794f698ae4c5084414efea0f5cc9f4ac562ec02d66e1484ff822ef97c2cadff"}, - {file = "lxml-5.4.0-cp313-cp313-manylinux_2_28_ppc64le.whl", hash = "sha256:2c62891b1ea3094bb12097822b3d44b93fc6c325f2043c4d2736a8ff09e65f60"}, - {file = "lxml-5.4.0-cp313-cp313-manylinux_2_28_s390x.whl", hash = "sha256:142accb3e4d1edae4b392bd165a9abdee8a3c432a2cca193df995bc3886249c8"}, - {file = "lxml-5.4.0-cp313-cp313-manylinux_2_28_x86_64.whl", hash = "sha256:1a42b3a19346e5601d1b8296ff6ef3d76038058f311902edd574461e9c036982"}, - {file = "lxml-5.4.0-cp313-cp313-musllinux_1_2_aarch64.whl", hash = "sha256:4291d3c409a17febf817259cb37bc62cb7eb398bcc95c1356947e2871911ae61"}, - {file = "lxml-5.4.0-cp313-cp313-musllinux_1_2_ppc64le.whl", hash = "sha256:4f5322cf38fe0e21c2d73901abf68e6329dc02a4994e483adbcf92b568a09a54"}, - {file = "lxml-5.4.0-cp313-cp313-musllinux_1_2_s390x.whl", hash = "sha256:0be91891bdb06ebe65122aa6bf3fc94489960cf7e03033c6f83a90863b23c58b"}, - {file = "lxml-5.4.0-cp313-cp313-musllinux_1_2_x86_64.whl", hash = "sha256:15a665ad90054a3d4f397bc40f73948d48e36e4c09f9bcffc7d90c87410e478a"}, - {file = "lxml-5.4.0-cp313-cp313-win32.whl", hash = "sha256:d5663bc1b471c79f5c833cffbc9b87d7bf13f87e055a5c86c363ccd2348d7e82"}, - {file = "lxml-5.4.0-cp313-cp313-win_amd64.whl", hash = "sha256:bcb7a1096b4b6b24ce1ac24d4942ad98f983cd3810f9711bcd0293f43a9d8b9f"}, - {file = "lxml-5.4.0-cp36-cp36m-macosx_10_9_x86_64.whl", hash = "sha256:7be701c24e7f843e6788353c055d806e8bd8466b52907bafe5d13ec6a6dbaecd"}, - {file = "lxml-5.4.0-cp36-cp36m-manylinux_2_12_i686.manylinux2010_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:fb54f7c6bafaa808f27166569b1511fc42701a7713858dddc08afdde9746849e"}, - {file = "lxml-5.4.0-cp36-cp36m-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:97dac543661e84a284502e0cf8a67b5c711b0ad5fb661d1bd505c02f8cf716d7"}, - {file = "lxml-5.4.0-cp36-cp36m-manylinux_2_28_x86_64.whl", hash = "sha256:c70e93fba207106cb16bf852e421c37bbded92acd5964390aad07cb50d60f5cf"}, - {file = "lxml-5.4.0-cp36-cp36m-manylinux_2_5_x86_64.manylinux1_x86_64.whl", hash = "sha256:9c886b481aefdf818ad44846145f6eaf373a20d200b5ce1a5c8e1bc2d8745410"}, - {file = "lxml-5.4.0-cp36-cp36m-musllinux_1_2_x86_64.whl", hash = "sha256:fa0e294046de09acd6146be0ed6727d1f42ded4ce3ea1e9a19c11b6774eea27c"}, - {file = "lxml-5.4.0-cp36-cp36m-win32.whl", hash = "sha256:61c7bbf432f09ee44b1ccaa24896d21075e533cd01477966a5ff5a71d88b2f56"}, - {file = "lxml-5.4.0-cp36-cp36m-win_amd64.whl", hash = "sha256:7ce1a171ec325192c6a636b64c94418e71a1964f56d002cc28122fceff0b6121"}, - {file = "lxml-5.4.0-cp37-cp37m-macosx_10_9_x86_64.whl", hash = "sha256:795f61bcaf8770e1b37eec24edf9771b307df3af74d1d6f27d812e15a9ff3872"}, - {file = "lxml-5.4.0-cp37-cp37m-manylinux_2_12_i686.manylinux2010_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:29f451a4b614a7b5b6c2e043d7b64a15bd8304d7e767055e8ab68387a8cacf4e"}, - {file = "lxml-5.4.0-cp37-cp37m-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:891f7f991a68d20c75cb13c5c9142b2a3f9eb161f1f12a9489c82172d1f133c0"}, - {file = "lxml-5.4.0-cp37-cp37m-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:4aa412a82e460571fad592d0f93ce9935a20090029ba08eca05c614f99b0cc92"}, - {file = "lxml-5.4.0-cp37-cp37m-manylinux_2_28_aarch64.whl", hash = "sha256:ac7ba71f9561cd7d7b55e1ea5511543c0282e2b6450f122672a2694621d63b7e"}, - {file = "lxml-5.4.0-cp37-cp37m-manylinux_2_28_x86_64.whl", hash = "sha256:c5d32f5284012deaccd37da1e2cd42f081feaa76981f0eaa474351b68df813c5"}, - {file = "lxml-5.4.0-cp37-cp37m-musllinux_1_2_aarch64.whl", hash = "sha256:ce31158630a6ac85bddd6b830cffd46085ff90498b397bd0a259f59d27a12188"}, - {file = "lxml-5.4.0-cp37-cp37m-musllinux_1_2_x86_64.whl", hash = "sha256:31e63621e073e04697c1b2d23fcb89991790eef370ec37ce4d5d469f40924ed6"}, - {file = "lxml-5.4.0-cp37-cp37m-win32.whl", hash = "sha256:be2ba4c3c5b7900246a8f866580700ef0d538f2ca32535e991027bdaba944063"}, - {file = "lxml-5.4.0-cp37-cp37m-win_amd64.whl", hash = "sha256:09846782b1ef650b321484ad429217f5154da4d6e786636c38e434fa32e94e49"}, - {file = "lxml-5.4.0-cp38-cp38-macosx_10_9_x86_64.whl", hash = "sha256:eaf24066ad0b30917186420d51e2e3edf4b0e2ea68d8cd885b14dc8afdcf6556"}, - {file = "lxml-5.4.0-cp38-cp38-manylinux_2_12_i686.manylinux2010_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:2b31a3a77501d86d8ade128abb01082724c0dfd9524f542f2f07d693c9f1175f"}, - {file = "lxml-5.4.0-cp38-cp38-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:0e108352e203c7afd0eb91d782582f00a0b16a948d204d4dec8565024fafeea5"}, - {file = "lxml-5.4.0-cp38-cp38-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:a11a96c3b3f7551c8a8109aa65e8594e551d5a84c76bf950da33d0fb6dfafab7"}, - {file = "lxml-5.4.0-cp38-cp38-manylinux_2_28_aarch64.whl", hash = "sha256:ca755eebf0d9e62d6cb013f1261e510317a41bf4650f22963474a663fdfe02aa"}, - {file = "lxml-5.4.0-cp38-cp38-manylinux_2_28_x86_64.whl", hash = "sha256:4cd915c0fb1bed47b5e6d6edd424ac25856252f09120e3e8ba5154b6b921860e"}, - {file = "lxml-5.4.0-cp38-cp38-musllinux_1_2_aarch64.whl", hash = "sha256:226046e386556a45ebc787871d6d2467b32c37ce76c2680f5c608e25823ffc84"}, - {file = "lxml-5.4.0-cp38-cp38-musllinux_1_2_x86_64.whl", hash = "sha256:b108134b9667bcd71236c5a02aad5ddd073e372fb5d48ea74853e009fe38acb6"}, - {file = "lxml-5.4.0-cp38-cp38-win32.whl", hash = "sha256:1320091caa89805df7dcb9e908add28166113dcd062590668514dbd510798c88"}, - {file = "lxml-5.4.0-cp38-cp38-win_amd64.whl", hash = "sha256:073eb6dcdf1f587d9b88c8c93528b57eccda40209cf9be549d469b942b41d70b"}, - {file = "lxml-5.4.0-cp39-cp39-macosx_10_9_universal2.whl", hash = "sha256:bda3ea44c39eb74e2488297bb39d47186ed01342f0022c8ff407c250ac3f498e"}, - {file = "lxml-5.4.0-cp39-cp39-macosx_10_9_x86_64.whl", hash = "sha256:9ceaf423b50ecfc23ca00b7f50b64baba85fb3fb91c53e2c9d00bc86150c7e40"}, - {file = "lxml-5.4.0-cp39-cp39-manylinux_2_12_i686.manylinux2010_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:664cdc733bc87449fe781dbb1f309090966c11cc0c0cd7b84af956a02a8a4729"}, - {file = "lxml-5.4.0-cp39-cp39-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:67ed8a40665b84d161bae3181aa2763beea3747f748bca5874b4af4d75998f87"}, - {file = "lxml-5.4.0-cp39-cp39-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:9b4a3bd174cc9cdaa1afbc4620c049038b441d6ba07629d89a83b408e54c35cd"}, - {file = "lxml-5.4.0-cp39-cp39-manylinux_2_28_aarch64.whl", hash = "sha256:b0989737a3ba6cf2a16efb857fb0dfa20bc5c542737fddb6d893fde48be45433"}, - {file = "lxml-5.4.0-cp39-cp39-manylinux_2_28_x86_64.whl", hash = "sha256:dc0af80267edc68adf85f2a5d9be1cdf062f973db6790c1d065e45025fa26140"}, - {file = "lxml-5.4.0-cp39-cp39-musllinux_1_2_aarch64.whl", hash = "sha256:639978bccb04c42677db43c79bdaa23785dc7f9b83bfd87570da8207872f1ce5"}, - {file = "lxml-5.4.0-cp39-cp39-musllinux_1_2_x86_64.whl", hash = "sha256:5a99d86351f9c15e4a901fc56404b485b1462039db59288b203f8c629260a142"}, - {file = "lxml-5.4.0-cp39-cp39-win32.whl", hash = "sha256:3e6d5557989cdc3ebb5302bbdc42b439733a841891762ded9514e74f60319ad6"}, - {file = "lxml-5.4.0-cp39-cp39-win_amd64.whl", hash = "sha256:a8c9b7f16b63e65bbba889acb436a1034a82d34fa09752d754f88d708eca80e1"}, - {file = "lxml-5.4.0-pp310-pypy310_pp73-macosx_10_15_x86_64.whl", hash = "sha256:1b717b00a71b901b4667226bba282dd462c42ccf618ade12f9ba3674e1fabc55"}, - {file = "lxml-5.4.0-pp310-pypy310_pp73-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:27a9ded0f0b52098ff89dd4c418325b987feed2ea5cc86e8860b0f844285d740"}, - {file = "lxml-5.4.0-pp310-pypy310_pp73-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:4b7ce10634113651d6f383aa712a194179dcd496bd8c41e191cec2099fa09de5"}, - {file = "lxml-5.4.0-pp310-pypy310_pp73-manylinux_2_28_aarch64.whl", hash = "sha256:53370c26500d22b45182f98847243efb518d268374a9570409d2e2276232fd37"}, - {file = "lxml-5.4.0-pp310-pypy310_pp73-manylinux_2_28_x86_64.whl", hash = "sha256:c6364038c519dffdbe07e3cf42e6a7f8b90c275d4d1617a69bb59734c1a2d571"}, - {file = "lxml-5.4.0-pp310-pypy310_pp73-win_amd64.whl", hash = "sha256:b12cb6527599808ada9eb2cd6e0e7d3d8f13fe7bbb01c6311255a15ded4c7ab4"}, - {file = "lxml-5.4.0-pp37-pypy37_pp73-macosx_10_9_x86_64.whl", hash = "sha256:5f11a1526ebd0dee85e7b1e39e39a0cc0d9d03fb527f56d8457f6df48a10dc0c"}, - {file = "lxml-5.4.0-pp37-pypy37_pp73-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:48b4afaf38bf79109bb060d9016fad014a9a48fb244e11b94f74ae366a64d252"}, - {file = "lxml-5.4.0-pp37-pypy37_pp73-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:de6f6bb8a7840c7bf216fb83eec4e2f79f7325eca8858167b68708b929ab2172"}, - {file = "lxml-5.4.0-pp37-pypy37_pp73-manylinux_2_28_aarch64.whl", hash = "sha256:5cca36a194a4eb4e2ed6be36923d3cffd03dcdf477515dea687185506583d4c9"}, - {file = "lxml-5.4.0-pp37-pypy37_pp73-manylinux_2_28_x86_64.whl", hash = "sha256:b7c86884ad23d61b025989d99bfdd92a7351de956e01c61307cb87035960bcb1"}, - {file = "lxml-5.4.0-pp37-pypy37_pp73-win_amd64.whl", hash = "sha256:53d9469ab5460402c19553b56c3648746774ecd0681b1b27ea74d5d8a3ef5590"}, - {file = "lxml-5.4.0-pp38-pypy38_pp73-macosx_10_9_x86_64.whl", hash = "sha256:56dbdbab0551532bb26c19c914848d7251d73edb507c3079d6805fa8bba5b706"}, - {file = "lxml-5.4.0-pp38-pypy38_pp73-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:14479c2ad1cb08b62bb941ba8e0e05938524ee3c3114644df905d2331c76cd57"}, - {file = "lxml-5.4.0-pp38-pypy38_pp73-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:32697d2ea994e0db19c1df9e40275ffe84973e4232b5c274f47e7c1ec9763cdd"}, - {file = "lxml-5.4.0-pp38-pypy38_pp73-manylinux_2_28_aarch64.whl", hash = "sha256:24f6df5f24fc3385f622c0c9d63fe34604893bc1a5bdbb2dbf5870f85f9a404a"}, - {file = "lxml-5.4.0-pp38-pypy38_pp73-manylinux_2_28_x86_64.whl", hash = "sha256:151d6c40bc9db11e960619d2bf2ec5829f0aaffb10b41dcf6ad2ce0f3c0b2325"}, - {file = "lxml-5.4.0-pp38-pypy38_pp73-win_amd64.whl", hash = "sha256:4025bf2884ac4370a3243c5aa8d66d3cb9e15d3ddd0af2d796eccc5f0244390e"}, - {file = "lxml-5.4.0-pp39-pypy39_pp73-macosx_10_15_x86_64.whl", hash = "sha256:9459e6892f59ecea2e2584ee1058f5d8f629446eab52ba2305ae13a32a059530"}, - {file = "lxml-5.4.0-pp39-pypy39_pp73-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:47fb24cc0f052f0576ea382872b3fc7e1f7e3028e53299ea751839418ade92a6"}, - {file = "lxml-5.4.0-pp39-pypy39_pp73-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:50441c9de951a153c698b9b99992e806b71c1f36d14b154592580ff4a9d0d877"}, - {file = "lxml-5.4.0-pp39-pypy39_pp73-manylinux_2_28_aarch64.whl", hash = "sha256:ab339536aa798b1e17750733663d272038bf28069761d5be57cb4a9b0137b4f8"}, - {file = "lxml-5.4.0-pp39-pypy39_pp73-manylinux_2_28_x86_64.whl", hash = "sha256:9776af1aad5a4b4a1317242ee2bea51da54b2a7b7b48674be736d463c999f37d"}, - {file = "lxml-5.4.0-pp39-pypy39_pp73-win_amd64.whl", hash = "sha256:63e7968ff83da2eb6fdda967483a7a023aa497d85ad8f05c3ad9b1f2e8c84987"}, - {file = "lxml-5.4.0.tar.gz", hash = "sha256:d12832e1dbea4be280b22fd0ea7c9b87f0d8fc51ba06e92dc62d52f804f78ebd"}, + {file = "lxml-6.1.0-cp310-cp310-macosx_10_9_universal2.whl", hash = "sha256:41dcc4c7b10484257cbd6c37b83ddb26df2b0e5aff5ac00d095689015af868ec"}, + {file = "lxml-6.1.0-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:a31286dbb5e74c8e9a5344465b77ab4c5bd511a253b355b5ca2fae7e579fafec"}, + {file = "lxml-6.1.0-cp310-cp310-manylinux2014_aarch64.manylinux_2_17_aarch64.whl", hash = "sha256:1bc4cc83fb7f66ffb16f74d6dd0162e144333fc36ebcce32246f80c8735b2551"}, + {file = "lxml-6.1.0-cp310-cp310-manylinux2014_x86_64.manylinux_2_17_x86_64.whl", hash = "sha256:20cf4d0651987c906a2f5cba4e3a8d6ba4bfdf973cfe2a96c0d6053888ea2ecd"}, + {file = "lxml-6.1.0-cp310-cp310-manylinux_2_26_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:ffb34ea45a82dd637c2c97ae1bbb920850c1e59bcae79ce1c15af531d83e7215"}, + {file = "lxml-6.1.0-cp310-cp310-manylinux_2_26_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:a1d9b99e5b2597e4f5aed2484fef835256fa1b68a19e4265c97628ef4bf8bcf4"}, + {file = "lxml-6.1.0-cp310-cp310-manylinux_2_28_i686.whl", hash = "sha256:d43aa26dcda363f21e79afa0668f5029ed7394b3bb8c92a6927a3d34e8b610ea"}, + {file = "lxml-6.1.0-cp310-cp310-manylinux_2_31_armv7l.whl", hash = "sha256:6262b87f9e5c1e5fe501d6c153247289af42eb44ad7660b9b3de17baaf92d6f6"}, + {file = "lxml-6.1.0-cp310-cp310-manylinux_2_38_riscv64.manylinux_2_39_riscv64.whl", hash = "sha256:d1392c569c032f78a11a25d1de1c43fff13294c793b39e19d84fade3045cbbc3"}, + {file = "lxml-6.1.0-cp310-cp310-musllinux_1_2_aarch64.whl", hash = "sha256:045e387d1f4f42a418380930fa3f45c73c9b392faf67e495e58902e68e8f44a7"}, + {file = "lxml-6.1.0-cp310-cp310-musllinux_1_2_armv7l.whl", hash = "sha256:9f93d5b8b07f73e8c77e3c6556a3db269918390c804b5e5fcdd4858232cc8f16"}, + {file = "lxml-6.1.0-cp310-cp310-musllinux_1_2_riscv64.whl", hash = "sha256:de550d129f18d8ab819651ffe4f38b1b713c7e116707de3c0c6400d0ef34fbc1"}, + {file = "lxml-6.1.0-cp310-cp310-musllinux_1_2_x86_64.whl", hash = "sha256:c08da09dc003c9e8c70e06b53a11db6fb3b250c21c4236b03c7d7b443c318e7a"}, + {file = "lxml-6.1.0-cp310-cp310-win32.whl", hash = "sha256:37448bf9c7d7adfc5254763901e2bbd6bb876228dfc1fc7f66e58c06368a7544"}, + {file = "lxml-6.1.0-cp310-cp310-win_amd64.whl", hash = "sha256:2593a0a6621545b9095b71ad74ed4226eba438a7d9fc3712a99bdb15508cf93a"}, + {file = "lxml-6.1.0-cp310-cp310-win_arm64.whl", hash = "sha256:e80807d72f96b96ad5588cb85c75616e4f2795a7737d4630784c51497beb7776"}, + {file = "lxml-6.1.0-cp311-cp311-macosx_10_9_universal2.whl", hash = "sha256:cec05be8c876f92a5aa07b01d60bbb4d11cfbdd654cad0561c0d7b5c043a61b9"}, + {file = "lxml-6.1.0-cp311-cp311-macosx_10_9_x86_64.whl", hash = "sha256:9c03e048b6ce8e77b09c734e931584894ecd58d08296804ca2d0b184c933ce50"}, + {file = "lxml-6.1.0-cp311-cp311-manylinux2014_aarch64.manylinux_2_17_aarch64.whl", hash = "sha256:942454ff253da14218f972b23dc72fa4edf6c943f37edd19cd697618b626fac5"}, + {file = "lxml-6.1.0-cp311-cp311-manylinux2014_x86_64.manylinux_2_17_x86_64.whl", hash = "sha256:d036ee7b99d5148072ac7c9b847193decdfeac633db350363f7bce4fff108f0e"}, + {file = "lxml-6.1.0-cp311-cp311-manylinux_2_26_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:3ae5d8d5427f3cc317e7950f2da7ad276df0cfa37b8de2f5658959e618ea8512"}, + {file = "lxml-6.1.0-cp311-cp311-manylinux_2_26_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:363e47283bde87051b821826e71dde47f107e08614e1aa312ba0c5711e77738c"}, + {file = "lxml-6.1.0-cp311-cp311-manylinux_2_28_i686.whl", hash = "sha256:f504d861d9f2a8f94020130adac88d66de93841707a23a86244263d1e54682f5"}, + {file = "lxml-6.1.0-cp311-cp311-manylinux_2_31_armv7l.whl", hash = "sha256:23a5dc68e08ed13331d61815c08f260f46b4a60fdd1640bbeb82cf89a9d90289"}, + {file = "lxml-6.1.0-cp311-cp311-manylinux_2_38_riscv64.manylinux_2_39_riscv64.whl", hash = "sha256:f15401d8d3dbf239e23c818afc10c7207f7b95f9a307e092122b6f86dd43209a"}, + {file = "lxml-6.1.0-cp311-cp311-musllinux_1_2_aarch64.whl", hash = "sha256:fcf3da95e93349e0647d48d4b36a12783105bcc74cb0c416952f9988410846a3"}, + {file = "lxml-6.1.0-cp311-cp311-musllinux_1_2_armv7l.whl", hash = "sha256:0d082495c5fcf426e425a6e28daaba1fcb6d8f854a4ff01effb1f1f381203eb9"}, + {file = "lxml-6.1.0-cp311-cp311-musllinux_1_2_riscv64.whl", hash = "sha256:e3c4f84b24a1fcba435157d111c4b755099c6ff00a3daee1ad281817de75ed11"}, + {file = "lxml-6.1.0-cp311-cp311-musllinux_1_2_x86_64.whl", hash = "sha256:976a6b39b1b13e8c354ad8d3f261f3a4ac6609518af91bdb5094760a08f132c4"}, + {file = "lxml-6.1.0-cp311-cp311-win32.whl", hash = "sha256:857efde87d365706590847b916baff69c0bc9252dc5af030e378c9800c0b10e3"}, + {file = "lxml-6.1.0-cp311-cp311-win_amd64.whl", hash = "sha256:183bfb45a493081943be7ea2b5adfc2b611e1cf377cefa8b8a8be404f45ef9a7"}, + {file = "lxml-6.1.0-cp311-cp311-win_arm64.whl", hash = "sha256:19f4164243fc206d12ed3d866e80e74f5bc3627966520da1a5f97e42c32a3f39"}, + {file = "lxml-6.1.0-cp312-cp312-macosx_10_13_universal2.whl", hash = "sha256:d2f17a16cd8751e8eb233a7e41aecdf8e511712e00088bf9be455f604cd0d28d"}, + {file = "lxml-6.1.0-cp312-cp312-macosx_10_13_x86_64.whl", hash = "sha256:f0cea5b1d3e6e77d71bd2b9972eb2446221a69dc52bb0b9c3c6f6e5700592d93"}, + {file = "lxml-6.1.0-cp312-cp312-manylinux2014_aarch64.manylinux_2_17_aarch64.whl", hash = "sha256:fc46da94826188ed45cb53bd8e3fc076ae22675aea2087843d4735627f867c6d"}, + {file = "lxml-6.1.0-cp312-cp312-manylinux2014_x86_64.manylinux_2_17_x86_64.whl", hash = "sha256:9147d8e386ec3b82c3b15d88927f734f565b0aaadef7def562b853adca45784a"}, + {file = "lxml-6.1.0-cp312-cp312-manylinux_2_26_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:5715e0e28736a070f3f34a7ccc09e2fdcba0e3060abbcf61a1a5718ff6d6b105"}, + {file = "lxml-6.1.0-cp312-cp312-manylinux_2_26_ppc64le.manylinux_2_28_ppc64le.whl", hash = "sha256:4937460dc5df0cdd2f06a86c285c28afda06aefa3af949f9477d3e8df430c485"}, + {file = "lxml-6.1.0-cp312-cp312-manylinux_2_26_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:bc783ee3147e60a25aa0445ea82b3e8aabb83b240f2b95d32cb75587ff781814"}, + {file = "lxml-6.1.0-cp312-cp312-manylinux_2_28_i686.whl", hash = "sha256:40d9189f80075f2e1f88db21ef815a2b17b28adf8e50aaf5c789bfe737027f32"}, + {file = "lxml-6.1.0-cp312-cp312-manylinux_2_31_armv7l.whl", hash = "sha256:05b9b8787e35bec69e68daf4952b2e6dfcfb0db7ecf1a06f8cdfbbac4eb71aad"}, + {file = "lxml-6.1.0-cp312-cp312-manylinux_2_38_riscv64.manylinux_2_39_riscv64.whl", hash = "sha256:0f0f08beb0182e3e9a86fae124b3c47a7b41b7b69b225e1377db983802404e54"}, + {file = "lxml-6.1.0-cp312-cp312-musllinux_1_2_aarch64.whl", hash = "sha256:73becf6d8c81d4c76b1014dbd3584cb26d904492dcf73ca85dc8bff08dcd6d2d"}, + {file = "lxml-6.1.0-cp312-cp312-musllinux_1_2_armv7l.whl", hash = "sha256:1ae225f66e5938f4fa29d37e009a3bb3b13032ac57eb4eb42afa44f6e4054e69"}, + {file = "lxml-6.1.0-cp312-cp312-musllinux_1_2_ppc64le.whl", hash = "sha256:690022c7fae793b0489aa68a658822cea83e0d5933781811cabbf5ea3bcfe73d"}, + {file = "lxml-6.1.0-cp312-cp312-musllinux_1_2_riscv64.whl", hash = "sha256:63aeafc26aac0be8aff14af7871249e87ea1319be92090bfd632ec68e03b16a5"}, + {file = "lxml-6.1.0-cp312-cp312-musllinux_1_2_x86_64.whl", hash = "sha256:264c605ab9c0e4aa1a679636f4582c4d3313700009fac3ec9c3412ed0d8f3e1d"}, + {file = "lxml-6.1.0-cp312-cp312-win32.whl", hash = "sha256:56971379bc5ee8037c5a0f09fa88f66cdb7d37c3e38af3e45cf539f41131ac1f"}, + {file = "lxml-6.1.0-cp312-cp312-win_amd64.whl", hash = "sha256:bba078de0031c219e5dd06cf3e6bf8fb8e6e64a77819b358f53bb132e3e03366"}, + {file = "lxml-6.1.0-cp312-cp312-win_arm64.whl", hash = "sha256:c3592631e652afa34999a088f98ba7dfc7d6aff0d535c410bea77a71743f3819"}, + {file = "lxml-6.1.0-cp313-cp313-macosx_10_13_universal2.whl", hash = "sha256:a0092f2b107b69601adf562a57c956fbb596e05e3e6651cabd3054113b007e45"}, + {file = "lxml-6.1.0-cp313-cp313-macosx_10_13_x86_64.whl", hash = "sha256:fc7140d7a7386e6b545d41b7358f4d02b656d4053f5fa6859f92f4b9c2572c4d"}, + {file = "lxml-6.1.0-cp313-cp313-manylinux2014_aarch64.manylinux_2_17_aarch64.whl", hash = "sha256:419c58fc92cc3a2c3fa5f78c63dbf5da70c1fa9c1b25f25727ecee89a96c7de2"}, + {file = "lxml-6.1.0-cp313-cp313-manylinux2014_x86_64.manylinux_2_17_x86_64.whl", hash = "sha256:37fabd1452852636cf38ecdcc9dd5ca4bba7a35d6c53fa09725deeb894a87491"}, + {file = "lxml-6.1.0-cp313-cp313-manylinux_2_26_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:a2853c8b2170cc6cd54a6b4d50d2c1a8a7aeca201f23804b4898525c7a152cfc"}, + {file = "lxml-6.1.0-cp313-cp313-manylinux_2_26_ppc64le.manylinux_2_28_ppc64le.whl", hash = "sha256:8e369cbd690e788c8d15e56222d91a09c6a417f49cbc543040cba0fe2e25a79e"}, + {file = "lxml-6.1.0-cp313-cp313-manylinux_2_26_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:e69aa6805905807186eb00e66c6d97a935c928275182eb02ee40ba00da9623b2"}, + {file = "lxml-6.1.0-cp313-cp313-manylinux_2_28_i686.whl", hash = "sha256:4bd1bdb8a9e0e2dd229de19b5f8aebac80e916921b4b2c6ef8a52bc131d0c1f9"}, + {file = "lxml-6.1.0-cp313-cp313-manylinux_2_31_armv7l.whl", hash = "sha256:cbd7b79cdcb4986ad78a2662625882747f09db5e4cd7b2ae178a88c9c51b3dfe"}, + {file = "lxml-6.1.0-cp313-cp313-manylinux_2_38_riscv64.manylinux_2_39_riscv64.whl", hash = "sha256:43e4d297f11080ec9d64a4b1ad7ac02b4484c9f0e2179d9c4ef78e886e747b88"}, + {file = "lxml-6.1.0-cp313-cp313-musllinux_1_2_aarch64.whl", hash = "sha256:cc16682cc987a3da00aa56a3aa3075b08edb10d9b1e476938cfdbee8f3b67181"}, + {file = "lxml-6.1.0-cp313-cp313-musllinux_1_2_armv7l.whl", hash = "sha256:d6d8efe71429635f0559579092bb5e60560d7b9115ee38c4adbea35632e7fa24"}, + {file = "lxml-6.1.0-cp313-cp313-musllinux_1_2_ppc64le.whl", hash = "sha256:7e39ab3a28af7784e206d8606ec0e4bcad0190f63a492bca95e94e5a4aef7f6e"}, + {file = "lxml-6.1.0-cp313-cp313-musllinux_1_2_riscv64.whl", hash = "sha256:9eb667bf50856c4a58145f8ca2d5e5be160191e79eb9e30855a476191b3c3495"}, + {file = "lxml-6.1.0-cp313-cp313-musllinux_1_2_x86_64.whl", hash = "sha256:7f4a77d6f7edf9230cee3e1f7f6764722a41604ee5681844f18db9a81ea0ec33"}, + {file = "lxml-6.1.0-cp313-cp313-win32.whl", hash = "sha256:28902146ffbe5222df411c5d19e5352490122e14447e98cd118907ee3fd6ee62"}, + {file = "lxml-6.1.0-cp313-cp313-win_amd64.whl", hash = "sha256:4a1503c56e4e2b38dc76f2f2da7bae69670c0f1933e27cfa34b2fa5876410b16"}, + {file = "lxml-6.1.0-cp313-cp313-win_arm64.whl", hash = "sha256:e0af85773850417d994d019741239b901b22c6680206f46a34766926e466141d"}, + {file = "lxml-6.1.0-cp314-cp314-macosx_10_15_universal2.whl", hash = "sha256:ab863fd37458fed6456525f297d21239d987800c46e67da5ef04fc6b3dd93ac8"}, + {file = "lxml-6.1.0-cp314-cp314-macosx_10_15_x86_64.whl", hash = "sha256:6fd8b1df8254ff4fd93fd31da1fc15770bde23ac045be9bb1f87425702f61cc9"}, + {file = "lxml-6.1.0-cp314-cp314-manylinux2014_aarch64.manylinux_2_17_aarch64.whl", hash = "sha256:47024feaae386a92a146af0d2aeed65229bf6fff738e6a11dda6b0015fb8fd03"}, + {file = "lxml-6.1.0-cp314-cp314-manylinux2014_x86_64.manylinux_2_17_x86_64.whl", hash = "sha256:3f00972f84450204cd5d93a5395965e348956aaceaadec693a22ec743f8ae3eb"}, + {file = "lxml-6.1.0-cp314-cp314-manylinux_2_26_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:97faa0860e13b05b15a51fb4986421ef7a30f0b3334061c416e0981e9450ca4c"}, + {file = "lxml-6.1.0-cp314-cp314-manylinux_2_26_ppc64le.manylinux_2_28_ppc64le.whl", hash = "sha256:972a6451204798675407beaad97b868d0c733d9a74dafefc63120b81b8c2de28"}, + {file = "lxml-6.1.0-cp314-cp314-manylinux_2_26_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:fe022f20bc4569ec66b63b3fb275a3d628d9d32da6326b2982584104db6d3086"}, + {file = "lxml-6.1.0-cp314-cp314-manylinux_2_28_i686.whl", hash = "sha256:75c4c7c619a744f972f4451bf5adf6d0fb00992a1ffc9fd78e13b0bc817cc99f"}, + {file = "lxml-6.1.0-cp314-cp314-manylinux_2_31_armv7l.whl", hash = "sha256:3648f20d25102a22b6061c688beb3a805099ea4beb0a01ce62975d926944d292"}, + {file = "lxml-6.1.0-cp314-cp314-manylinux_2_38_riscv64.manylinux_2_39_riscv64.whl", hash = "sha256:77b9f99b17cbf14026d1e618035077060fc7195dd940d025149f3e2e830fbfcb"}, + {file = "lxml-6.1.0-cp314-cp314-musllinux_1_2_aarch64.whl", hash = "sha256:32662519149fd7a9db354175aa5e417d83485a8039b8aaa62f873ceee7ea4cad"}, + {file = "lxml-6.1.0-cp314-cp314-musllinux_1_2_armv7l.whl", hash = "sha256:73d658216fc173cf2c939e90e07b941c5e12736b0bf6a99e7af95459cfe8eabb"}, + {file = "lxml-6.1.0-cp314-cp314-musllinux_1_2_ppc64le.whl", hash = "sha256:ac4db068889f8772a4a698c5980ec302771bb545e10c4b095d4c8be26749616f"}, + {file = "lxml-6.1.0-cp314-cp314-musllinux_1_2_riscv64.whl", hash = "sha256:45e9dfbd1b661eb64ba0d4dbe762bd210c42d86dd1e5bd2bdf89d634231beb43"}, + {file = "lxml-6.1.0-cp314-cp314-musllinux_1_2_x86_64.whl", hash = "sha256:89e8d73d09ac696a5ba42ec69787913d53284f12092f651506779314f10ba585"}, + {file = "lxml-6.1.0-cp314-cp314-win32.whl", hash = "sha256:ebe33f4ec1b2de38ceb225a1749a2965855bffeef435ba93cd2d5d540783bf2f"}, + {file = "lxml-6.1.0-cp314-cp314-win_amd64.whl", hash = "sha256:398443df51c538bd578529aa7e5f7afc6c292644174b47961f3bf87fe5741120"}, + {file = "lxml-6.1.0-cp314-cp314-win_arm64.whl", hash = "sha256:8c8984e1d8c4b3949e419158fda14d921ff703a9ed8a47236c6eb7a2b6cb4946"}, + {file = "lxml-6.1.0-cp314-cp314t-macosx_10_15_universal2.whl", hash = "sha256:1081dd10bc6fa437db2500e13993abf7cc30716d0a2f40e65abb935f02ec559c"}, + {file = "lxml-6.1.0-cp314-cp314t-macosx_10_15_x86_64.whl", hash = "sha256:dabecc48db5f42ba348d1f5d5afdc54c6c4cc758e676926c7cd327045749517d"}, + {file = "lxml-6.1.0-cp314-cp314t-manylinux2014_aarch64.manylinux_2_17_aarch64.whl", hash = "sha256:e3dd5fe19c9e0ac818a9c7f132a5e43c1339ec1cbbfecb1a938bd3a47875b7c9"}, + {file = "lxml-6.1.0-cp314-cp314t-manylinux2014_x86_64.manylinux_2_17_x86_64.whl", hash = "sha256:9e7b0a4ca6dcc007a4cef00a761bba2dea959de4bd2df98f926b33c92ca5dfb9"}, + {file = "lxml-6.1.0-cp314-cp314t-manylinux_2_26_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:5d27bbe326c6b539c64b42638b18bc6003a8d88f76213a97ac9ed4f885efeab7"}, + {file = "lxml-6.1.0-cp314-cp314t-manylinux_2_26_ppc64le.manylinux_2_28_ppc64le.whl", hash = "sha256:c4e425db0c5445ef0ad56b0eec54f89b88b2d884656e536a90b2f52aecb4ca86"}, + {file = "lxml-6.1.0-cp314-cp314t-manylinux_2_26_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:4b89b098105b8599dc57adac95d1813409ac476d3c948a498775d3d0c6124bfb"}, + {file = "lxml-6.1.0-cp314-cp314t-manylinux_2_28_i686.whl", hash = "sha256:c4a699432846df86cc3de502ee85f445ebad748a1c6021d445f3e514d2cd4b1c"}, + {file = "lxml-6.1.0-cp314-cp314t-manylinux_2_31_armv7l.whl", hash = "sha256:30e7b2ed63b6c8e97cca8af048589a788ab5c9c905f36d9cf1c2bb549f450d2f"}, + {file = "lxml-6.1.0-cp314-cp314t-manylinux_2_38_riscv64.manylinux_2_39_riscv64.whl", hash = "sha256:022981127642fe19866d2907d76241bb07ed21749601f727d5d5dd1ce5d1b773"}, + {file = "lxml-6.1.0-cp314-cp314t-musllinux_1_2_aarch64.whl", hash = "sha256:23cad0cc86046d4222f7f418910e46b89971c5a45d3c8abfad0f64b7b05e4a9b"}, + {file = "lxml-6.1.0-cp314-cp314t-musllinux_1_2_armv7l.whl", hash = "sha256:21c3302068f50d1e8728c67c87ba92aa87043abee517aa2576cca1855326b405"}, + {file = "lxml-6.1.0-cp314-cp314t-musllinux_1_2_ppc64le.whl", hash = "sha256:be10838781cb3be19251e276910cd508fe127e27c3242e50521521a0f3781690"}, + {file = "lxml-6.1.0-cp314-cp314t-musllinux_1_2_riscv64.whl", hash = "sha256:2173a7bffe97667bbf0767f8a99e587740a8c56fdf3befac4b09cb29a80276fd"}, + {file = "lxml-6.1.0-cp314-cp314t-musllinux_1_2_x86_64.whl", hash = "sha256:c6854e9cf99c84beb004eecd7d3a3868ef1109bf2b1df92d7bc11e96a36c2180"}, + {file = "lxml-6.1.0-cp314-cp314t-win32.whl", hash = "sha256:00750d63ef0031a05331b9223463b1c7c02b9004cef2346a5b2877f0f9494dd2"}, + {file = "lxml-6.1.0-cp314-cp314t-win_amd64.whl", hash = "sha256:80410c3a7e3c617af04de17caa9f9f20adaa817093293d69eae7d7d0522836f5"}, + {file = "lxml-6.1.0-cp314-cp314t-win_arm64.whl", hash = "sha256:26dd9f57ee3bd41e7d35b4c98a2ffd89ed11591649f421f0ec19f67d50ec67ac"}, + {file = "lxml-6.1.0-cp38-cp38-macosx_10_9_x86_64.whl", hash = "sha256:b6c2f225662bc5ad416bdd06f72ca301b31b39ce4261f0e0097017fc2891b940"}, + {file = "lxml-6.1.0-cp38-cp38-manylinux2014_x86_64.manylinux_2_17_x86_64.whl", hash = "sha256:a86f06f059e22a0d574990ee2df24ede03f7f3c68c1336293eee9536c4c776cd"}, + {file = "lxml-6.1.0-cp38-cp38-manylinux_2_26_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:468479e52ecf3ec23799c863336d02c05fc2f7ffd1a1424eeeb9a28d4eb69d13"}, + {file = "lxml-6.1.0-cp38-cp38-manylinux_2_28_i686.whl", hash = "sha256:a02ca8fe48815bddcfca3248efe54451abb9dbf2f7d1c5744c8aa4142d476919"}, + {file = "lxml-6.1.0-cp38-cp38-musllinux_1_2_x86_64.whl", hash = "sha256:bb40648d96157f9081886defe13eac99253e663be969ff938a9289eff6e47b72"}, + {file = "lxml-6.1.0-cp38-cp38-win32.whl", hash = "sha256:1dd6a1c3ad4cb674f44525d9957f3e9c209bb6dd9213245195167a281fcc2bdc"}, + {file = "lxml-6.1.0-cp38-cp38-win_amd64.whl", hash = "sha256:4e2c54d6b47361d0f1d3bc8d4e082ad87201e56ccdcca4d3b9ee3644ff595ec8"}, + {file = "lxml-6.1.0-cp39-cp39-macosx_10_9_universal2.whl", hash = "sha256:920354904d1cb86577d4b3cfe2830c2dbe81d6f4449e57ada428f1609b5985f7"}, + {file = "lxml-6.1.0-cp39-cp39-macosx_10_9_x86_64.whl", hash = "sha256:c871299c595ee004d186f61840f0bfc4941aa3f17c8ba4a565ead7e4f4f820ee"}, + {file = "lxml-6.1.0-cp39-cp39-manylinux2014_aarch64.manylinux_2_17_aarch64.whl", hash = "sha256:d0d799ff958655781296ec870d5e2448e75150da2b3d07f13ff5b0c2c35beefd"}, + {file = "lxml-6.1.0-cp39-cp39-manylinux2014_x86_64.manylinux_2_17_x86_64.whl", hash = "sha256:7ba11752e346bd804ea312ec2eea2532dfa8b8d3261d81a32ef9e6ab16256280"}, + {file = "lxml-6.1.0-cp39-cp39-manylinux_2_26_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:26c5272c6a4bf4cf32d3f5a7890c942b0e04438691157d341616d02cca74d4bd"}, + {file = "lxml-6.1.0-cp39-cp39-manylinux_2_26_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:c53fa3a5a52122d590e847a57ccf955557b9634a7f99ff5a35131321b0a85317"}, + {file = "lxml-6.1.0-cp39-cp39-manylinux_2_28_i686.whl", hash = "sha256:76b958b4ea3104483c20f74866d55aa056546e15ebe83dd7aecd63698f43b755"}, + {file = "lxml-6.1.0-cp39-cp39-manylinux_2_31_armv7l.whl", hash = "sha256:8c11b984b5ce6add4dccc7144c7be5d364d298f15b0c6a57da1991baedc750ce"}, + {file = "lxml-6.1.0-cp39-cp39-manylinux_2_38_riscv64.manylinux_2_39_riscv64.whl", hash = "sha256:d3829a6e6fd550a219564912d4002c537f65da4c6ae4e093cc34462f4fa027ad"}, + {file = "lxml-6.1.0-cp39-cp39-musllinux_1_2_aarch64.whl", hash = "sha256:52b0ac6903cf74ebf997eb8c682d2fbac7d1ab7e4c552413eec55868a9b73f39"}, + {file = "lxml-6.1.0-cp39-cp39-musllinux_1_2_armv7l.whl", hash = "sha256:29f5c00cb7d752bce2c70ebd2d31b0a42f9499ffdd3ecb2f31a5b73ee43031ad"}, + {file = "lxml-6.1.0-cp39-cp39-musllinux_1_2_riscv64.whl", hash = "sha256:c748ebcb6877de89f48ab90ca96642ac458fff5dec291a2b9337cd4d0934e383"}, + {file = "lxml-6.1.0-cp39-cp39-musllinux_1_2_x86_64.whl", hash = "sha256:08950a23f296b3f83521577274e3d3b0f3d739bf2e68d01a752e4288bc50d286"}, + {file = "lxml-6.1.0-cp39-cp39-win32.whl", hash = "sha256:11a873c77a181b4fef9c2e357d08ed399542c2af1390101da66720a19c7c9618"}, + {file = "lxml-6.1.0-cp39-cp39-win_amd64.whl", hash = "sha256:81ff55c70b67d19d52b6fd118a114c0a4c97d799cd3089ff9bd9e2ff4b414ee2"}, + {file = "lxml-6.1.0-cp39-cp39-win_arm64.whl", hash = "sha256:481d6e2104285d9add34f41b42b247b76b61c5b5c26c303c2e9707bbf8bd9a64"}, + {file = "lxml-6.1.0-pp311-pypy311_pp73-macosx_10_15_x86_64.whl", hash = "sha256:546b66c0dd1bb8d9fa89d7123e5fa19a8aff3a1f2141eb22df96112afb17b842"}, + {file = "lxml-6.1.0-pp311-pypy311_pp73-manylinux2014_aarch64.manylinux_2_17_aarch64.whl", hash = "sha256:5cfa1a34df366d9dc0d5eaf420f4cf2bb1e1bebe1066d1c2fc28c179f8a4004c"}, + {file = "lxml-6.1.0-pp311-pypy311_pp73-manylinux2014_x86_64.manylinux_2_17_x86_64.whl", hash = "sha256:db88156fcf544cdbf0d95588051515cfdfd4c876fc66444eb98bceb5d6db76de"}, + {file = "lxml-6.1.0-pp311-pypy311_pp73-manylinux_2_26_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:07f98f5496f96bf724b1e3c933c107f0cbf2745db18c03d2e13a291c3afd2635"}, + {file = "lxml-6.1.0-pp311-pypy311_pp73-manylinux_2_26_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:4642e04449a1e164b5ff71ffd901ddb772dfabf5c9adf1b7be5dffe1212bc037"}, + {file = "lxml-6.1.0-pp311-pypy311_pp73-win_amd64.whl", hash = "sha256:7da13bb6fbadfafb474e0226a30570a3445cfd47c86296f2446dafbd77079ace"}, + {file = "lxml-6.1.0.tar.gz", hash = "sha256:bfd57d8008c4965709a919c3e9a98f76c2c7cb319086b3d26858250620023b13"}, ] [package.extras] @@ -827,7 +829,6 @@ cssselect = ["cssselect (>=0.7)"] html-clean = ["lxml_html_clean"] html5 = ["html5lib"] htmlsoup = ["BeautifulSoup4"] -source = ["Cython (>=3.0.11,<3.1.0)"] [[package]] name = "markupsafe" @@ -1462,4 +1463,4 @@ zstd = ["backports-zstd (>=1.0.0) ; python_version < \"3.14\""] [metadata] lock-version = "2.1" python-versions = "^3.13" -content-hash = "8963caf3dd6cb8844ff50e010e94a986dbcc5a6d1a2fad9ecb23864c6f14f203" +content-hash = "9493920814bad3a0763832082581dbe8c764881c117c8dc6f14138a684b4ee7c" diff --git a/pyproject.toml b/pyproject.toml index f2652cc9..a753d884 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -24,7 +24,7 @@ deepdiff = "^8.6.2" semver = "^3.0" gitpython = "^3.1.46" pytest-xdist = "^3.0" -lxml = "^5.0" +lxml = ">=5,<7" boto3 = "^1.37.38" ansible-core = "^2.19.8" urllib3 = "^2.6.3" From c439c7e905bf83d770a83f093d417dbb15d6f89f Mon Sep 17 00:00:00 2001 From: Valswyn-NHS Date: Thu, 23 Apr 2026 14:25:53 +0100 Subject: [PATCH 11/38] APM-7202-Dependabot-interval --- .github/dependabot.yml | 24 ++++++++++++------------ 1 file changed, 12 insertions(+), 12 deletions(-) diff --git a/.github/dependabot.yml b/.github/dependabot.yml index b5ec0a17..d5644b45 100644 --- a/.github/dependabot.yml +++ b/.github/dependabot.yml @@ -7,13 +7,13 @@ updates: - package-ecosystem: "pip" directory: "/" schedule: - interval: "daily" + interval: "weekly" target-branch: "master" - labels: ["dependencies", "python", "poetry"] + labels: [ "dependencies", "python", "poetry" ] open-pull-requests-limit: 10 ignore: - dependency-name: "*" - update-types: ["version-update:semver-major"] + update-types: [ "version-update:semver-major" ] # --------------------------- # NodeJS (root) @@ -21,13 +21,13 @@ updates: - package-ecosystem: "npm" directory: "/" schedule: - interval: "daily" + interval: "weekly" target-branch: "master" - labels: ["dependencies", "npm"] + labels: [ "dependencies", "npm" ] open-pull-requests-limit: 10 ignore: - dependency-name: "*" - update-types: ["version-update:semver-major"] + update-types: [ "version-update:semver-major" ] # --------------------------- # NodeJS (sandbox/) @@ -35,13 +35,13 @@ updates: - package-ecosystem: "npm" directory: "/sandbox" schedule: - interval: "daily" + interval: "weekly" target-branch: "master" - labels: ["dependencies", "npm", "sandbox"] + labels: [ "dependencies", "npm", "sandbox" ] open-pull-requests-limit: 10 ignore: - dependency-name: "*" - update-types: ["version-update:semver-major"] + update-types: [ "version-update:semver-major" ] # --------------------------- # GitHub Actions @@ -49,8 +49,8 @@ updates: - package-ecosystem: "github-actions" directory: "/" schedule: - interval: "daily" + interval: "weekly" target-branch: "master" - labels: ["dependencies", "github-actions"] + labels: [ "dependencies", "github-actions" ] cooldown: - default-days: 7 \ No newline at end of file + default-days: 7 From f9772d54e3a00a3faa98a8b0078069c1dfc137cc Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Thu, 30 Apr 2026 04:54:21 +0000 Subject: [PATCH 12/38] Bump gitpython from 3.1.47 to 3.1.49 (#596) Bumps [gitpython](https://github.com/gitpython-developers/GitPython) from 3.1.47 to 3.1.49. - [Release notes](https://github.com/gitpython-developers/GitPython/releases) - [Changelog](https://github.com/gitpython-developers/GitPython/blob/main/CHANGES) - [Commits](https://github.com/gitpython-developers/GitPython/compare/3.1.47...3.1.49) --- updated-dependencies: - dependency-name: gitpython dependency-version: 3.1.49 dependency-type: direct:production update-type: version-update:semver-patch ... Signed-off-by: dependabot[bot] Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> --- poetry.lock | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/poetry.lock b/poetry.lock index ae08373d..d596ef1b 100644 --- a/poetry.lock +++ b/poetry.lock @@ -606,14 +606,14 @@ smmap = ">=3.0.1,<6" [[package]] name = "gitpython" -version = "3.1.47" +version = "3.1.49" description = "GitPython is a Python library used to interact with Git repositories" optional = false python-versions = ">=3.7" groups = ["main"] files = [ - {file = "gitpython-3.1.47-py3-none-any.whl", hash = "sha256:489f590edfd6d20571b2c0e72c6a6ac6915ee8b8cd04572330e3842207a78905"}, - {file = "gitpython-3.1.47.tar.gz", hash = "sha256:dba27f922bd2b42cb54c87a8ab3cb6beb6bf07f3d564e21ac848913a05a8a3cd"}, + {file = "gitpython-3.1.49-py3-none-any.whl", hash = "sha256:024b0422d7f84d15cd794844e029ffebd4c5d42a7eb9b936b458697ef550a02c"}, + {file = "gitpython-3.1.49.tar.gz", hash = "sha256:42f9399c9eb33fc581014bedd76049dfbaf6375aa2a5754575966387280315e1"}, ] [package.dependencies] From 1bfacb74a2c1fac8d90b2522f3289f500c58ed76 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Thu, 7 May 2026 04:53:11 +0000 Subject: [PATCH 13/38] Bump gitpython from 3.1.49 to 3.1.50 (#598) Bumps [gitpython](https://github.com/gitpython-developers/GitPython) from 3.1.49 to 3.1.50. - [Release notes](https://github.com/gitpython-developers/GitPython/releases) - [Changelog](https://github.com/gitpython-developers/GitPython/blob/main/CHANGES) - [Commits](https://github.com/gitpython-developers/GitPython/compare/3.1.49...3.1.50) --- updated-dependencies: - dependency-name: gitpython dependency-version: 3.1.50 dependency-type: direct:production update-type: version-update:semver-patch ... Signed-off-by: dependabot[bot] Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> --- poetry.lock | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/poetry.lock b/poetry.lock index d596ef1b..9f18d54b 100644 --- a/poetry.lock +++ b/poetry.lock @@ -606,14 +606,14 @@ smmap = ">=3.0.1,<6" [[package]] name = "gitpython" -version = "3.1.49" +version = "3.1.50" description = "GitPython is a Python library used to interact with Git repositories" optional = false python-versions = ">=3.7" groups = ["main"] files = [ - {file = "gitpython-3.1.49-py3-none-any.whl", hash = "sha256:024b0422d7f84d15cd794844e029ffebd4c5d42a7eb9b936b458697ef550a02c"}, - {file = "gitpython-3.1.49.tar.gz", hash = "sha256:42f9399c9eb33fc581014bedd76049dfbaf6375aa2a5754575966387280315e1"}, + {file = "gitpython-3.1.50-py3-none-any.whl", hash = "sha256:d352abe2908d07355014abdd21ddf798c2a961469239afec4962e9da884858f9"}, + {file = "gitpython-3.1.50.tar.gz", hash = "sha256:80da2d12504d52e1f998772dc5baf6e553f8d2fcfe1fcc226c9d9a2ee3372dcc"}, ] [package.dependencies] From 0a40a3bf6252ac6e94310d324d0cb824b7232bd4 Mon Sep 17 00:00:00 2001 From: Sathiyakumar Ganesan Date: Mon, 11 May 2026 09:33:33 +0100 Subject: [PATCH 14/38] Refactor YAML parameters and improve variable handling across multiple files --- azure/build-and-push-ecs-proxies.yml | 22 +- azure/build-prereqs.yml | 22 +- azure/common/apigee-build.yml | 189 +++++++++--------- azure/components/aws-assume-role.yml | 132 ++++++------ .../components/cleanup-ecs-pr-proxies-job.yml | 5 +- azure/components/curl.yml | 14 +- azure/components/get-access-token.yml | 26 ++- 7 files changed, 211 insertions(+), 199 deletions(-) diff --git a/azure/build-and-push-ecs-proxies.yml b/azure/build-and-push-ecs-proxies.yml index 51f70518..a6047e43 100644 --- a/azure/build-and-push-ecs-proxies.yml +++ b/azure/build-and-push-ecs-proxies.yml @@ -1,17 +1,21 @@ parameters: - - name: 'container_vars' + - name: "container_vars" type: string - default: 'ecs-proxies-containers.yml' - - name: 'env_vars_dir' + default: "ecs-proxies-containers.yml" + - name: "env_vars_dir" type: string - default: './' - - name: 'utils_dir' + default: "./" + - name: "utils_dir" type: string - default: 'utils' + default: "utils" steps: - bash: | - source "${{ parameters.env_vars_dir }}/.build_env_vars" - CONTAINER_VARS_FILE="$(realpath ${{ parameters.container_vars }})" \ - make --no-print-directory -C ${{ parameters.utils_dir }}/ansible build-ecs-proxies + source "$env_vars_dir/.build_env_vars" + CONTAINER_VARS_FILE="$(realpath "$container_vars")" \ + make --no-print-directory -C "$utils_dir/ansible" build-ecs-proxies + env: + env_vars_dir: ${{ parameters.env_vars_dir }} + container_vars: ${{ parameters.container_vars }} + utils_dir: ${{ parameters.utils_dir }} displayName: "Build and push ECS proxies" diff --git a/azure/build-prereqs.yml b/azure/build-prereqs.yml index 11374528..79dbd93f 100644 --- a/azure/build-prereqs.yml +++ b/azure/build-prereqs.yml @@ -1,7 +1,7 @@ parameters: - - name: 'utils_dir' + - name: "utils_dir" type: string - default: 'utils' + default: "utils" steps: - bash: | @@ -13,21 +13,13 @@ steps: echo "##vso[task.setvariable variable=PY_VER]$PATCH" echo "Resolved latest python version: $PATCH" echo "##vso[task.setvariable variable=LD_LIBRARY_PATH;]/agent/_work/_tool/Python/${PATCH}/x64/lib/" - displayName: 'Query and set python tool cache path' - + displayName: "Query and set python tool cache path" - task: UsePythonVersion@0 name: UsePy - displayName: 'Use Python 3.13' + displayName: "Use Python 3.13" inputs: - versionSpec: '$(PY_VER)' - - # - bash: | - # echo "Checking the python version in use to set LD_LIBRARY_PATH" - # echo "Python location: $(UsePy.pythonLocation)" - # echo "##vso[task.setvariable variable=LD_LIBRARY_PATH]$(UsePy.pythonLocation)/lib" - # displayName: 'Set LD_LIBRARY_PATH' - + versionSpec: "$(PY_VER)" - bash: | tfenv use 0.14.6 @@ -41,6 +33,8 @@ steps: if ! hash poetry 2>/dev/null; then pip3 install poetry fi - cd ${{ parameters.utils_dir }} + cd "$utils_dir" make install + env: + utils_dir: ${{ parameters.utils_dir }} displayName: "Install Utils dependencies" diff --git a/azure/common/apigee-build.yml b/azure/common/apigee-build.yml index 873fe838..b35c1290 100644 --- a/azure/common/apigee-build.yml +++ b/azure/common/apigee-build.yml @@ -45,10 +45,14 @@ jobs: variables: ${{ each var in parameters.variables }}: ${{ var.key }}: ${{ var.value }} - steps: + env: + service_name: ${{ parameters.service_name }} + service_short_name: ${{ parameters.short_service_name }} + python_version: ${{ parameters.python_version }} + steps: - bash: | - if [ ! -z "$(ls -A \"$(Pipeline.Workspace)/s/${{ parameters.service_name }}\" 2>/dev/null)" ]; then + if [ -n "$(ls -A "$(Pipeline.Workspace)/s/$service_name" 2>/dev/null)" ]; then echo "workspace directory is not empty!" exit 1 fi @@ -67,63 +71,63 @@ jobs: - template: ../components/aws-clean-config.yml - ${{ if parameters.notify }}: - - template: ../components/aws-assume-role.yml - parameters: - role: "auto-ops" - profile: "apm_ptl" - - - template: ../components/get-aws-secrets-and-ssm-params.yml - parameters: - secret_file_ids: - - ${{ each secret_file_id in parameters.secret_file_ids }}: - - ${{ secret_file_id }} - secret_ids: - - ptl/access-tokens/github/repo-status-update/GITHUB_ACCESS_TOKEN - - ${{ each secret_id in parameters.secret_ids }}: - - ${{ secret_id }} - config_ids: - - /ptl/azure-devops/GITHUB_USER - - ${{ each config_id in parameters.config_ids }}: - - ${{ config_id }} - - - bash: | - echo "Build.SourceBranch: $(Build.SourceBranch)" - echo "Build.SourceBranchName: $(Build.SourceBranchName)" - echo "Build.SourceVersion: $(Build.SourceVersion)" - echo "Build.SourceVersionMessage: $(Build.SourceVersionMessage)" - - if [[ ! -z $(NOTIFY_COMMIT_SHA) ]]; then - echo "##[debug]Using already provided NOTIFY_COMMIT_SHA=$(NOTIFY_COMMIT_SHA)" - else - NOTIFY_COMMIT_SHA="" - - if [[ "$(Build.SourceBranch)" =~ ^refs/tags/.+$ ]]; then - echo "##[debug]Build appears to be a tag build" - echo "##[debug]Using Build.SourceVersion as NOTIFY_COMMIT_SHA" - NOTIFY_COMMIT_SHA="$(Build.SourceVersion)" - fi - - if [[ "$(Build.SourceBranch)" =~ ^refs/pull/.+$ ]]; then - echo "##[debug]Build appears to be a pull request build" - echo "##[debug]Extracting NOTIFY_COMMIT_SHA from Build.SourceVersionMessage" - NOTIFY_COMMIT_SHA=`echo "$(Build.SourceVersionMessage)" | cut -d' ' -f2` - fi - - if [[ -z $NOTIFY_COMMIT_SHA ]]; then - echo "##[debug]Build does not appear to be pull or tag build" - echo "##[debug]Using Build.SourceVersion as NOTIFY_COMMIT_SHA" - NOTIFY_COMMIT_SHA="$(Build.SourceVersion)" - fi - - echo "##vso[task.setvariable variable=NOTIFY_COMMIT_SHA]$NOTIFY_COMMIT_SHA" - fi - displayName: Set NOTIFY_COMMIT_SHA - condition: always() - - - template: '../components/update-github-status.yml' - parameters: - state: pending - description: "Build started" + - template: ../components/aws-assume-role.yml + parameters: + role: "auto-ops" + profile: "apm_ptl" + + - template: ../components/get-aws-secrets-and-ssm-params.yml + parameters: + secret_file_ids: + - ${{ each secret_file_id in parameters.secret_file_ids }}: + - ${{ secret_file_id }} + secret_ids: + - ptl/access-tokens/github/repo-status-update/GITHUB_ACCESS_TOKEN + - ${{ each secret_id in parameters.secret_ids }}: + - ${{ secret_id }} + config_ids: + - /ptl/azure-devops/GITHUB_USER + - ${{ each config_id in parameters.config_ids }}: + - ${{ config_id }} + + - bash: | + echo "Build.SourceBranch: $(Build.SourceBranch)" + echo "Build.SourceBranchName: $(Build.SourceBranchName)" + echo "Build.SourceVersion: $(Build.SourceVersion)" + echo "Build.SourceVersionMessage: $(Build.SourceVersionMessage)" + + if [[ ! -z $(NOTIFY_COMMIT_SHA) ]]; then + echo "##[debug]Using already provided NOTIFY_COMMIT_SHA=$(NOTIFY_COMMIT_SHA)" + else + NOTIFY_COMMIT_SHA="" + + if [[ "$(Build.SourceBranch)" =~ ^refs/tags/.+$ ]]; then + echo "##[debug]Build appears to be a tag build" + echo "##[debug]Using Build.SourceVersion as NOTIFY_COMMIT_SHA" + NOTIFY_COMMIT_SHA="$(Build.SourceVersion)" + fi + + if [[ "$(Build.SourceBranch)" =~ ^refs/pull/.+$ ]]; then + echo "##[debug]Build appears to be a pull request build" + echo "##[debug]Extracting NOTIFY_COMMIT_SHA from Build.SourceVersionMessage" + NOTIFY_COMMIT_SHA=`echo "$(Build.SourceVersionMessage)" | cut -d' ' -f2` + fi + + if [[ -z $NOTIFY_COMMIT_SHA ]]; then + echo "##[debug]Build does not appear to be pull or tag build" + echo "##[debug]Using Build.SourceVersion as NOTIFY_COMMIT_SHA" + NOTIFY_COMMIT_SHA="$(Build.SourceVersion)" + fi + + echo "##vso[task.setvariable variable=NOTIFY_COMMIT_SHA]$NOTIFY_COMMIT_SHA" + fi + displayName: Set NOTIFY_COMMIT_SHA + condition: always() + + - template: "../components/update-github-status.yml" + parameters: + state: pending + description: "Build started" - bash: | if [[ ! -z $(UTILS_PR_NUMBER) ]]; then @@ -144,7 +148,7 @@ jobs: service_name: "${{ parameters.service_name }}" - bash: | - pyversion=${{ parameters.python_version }} + pyversion="$python_version" PATCH=$(curl -fsSL https://raw.githubusercontent.com/actions/python-versions/main/versions-manifest.json \ | jq -r --arg pyversion "$pyversion" '[ .[] | select(.stable == true) | .version | select(test("^" + ($pyversion|gsub("\\.";"\\.")) + "\\.\\d+$")) | split(".") | map(tonumber)] | max | join(".")') @@ -152,20 +156,13 @@ jobs: echo "##vso[task.setvariable variable=PY_VER]$PATCH" echo "Resolved latest python version: $PATCH" echo "##vso[task.setvariable variable=LD_LIBRARY_PATH;]/agent/_work/_tool/Python/${PATCH}/x64/lib/" - displayName: 'Query and set python tool cache path' - + displayName: "Query and set python tool cache path" - task: UsePythonVersion@0 name: UsePy - displayName: 'Use Python ${{ parameters.python_version }}' + displayName: "Use Python ${{ parameters.python_version }}" inputs: - versionSpec: '$(PY_VER)' - - # - bash: | - # echo "Checking the python version in use to set LD_LIBRARY_PATH" - # echo "Python location: $(UsePy.pythonLocation)" - # echo "##vso[task.setvariable variable=LD_LIBRARY_PATH]$(UsePy.pythonLocation)/lib" - # displayName: 'Set LD_LIBRARY_PATH' + versionSpec: "$(PY_VER)" - ${{ each cache_step in parameters.cache_steps }}: - ${{ cache_step }} @@ -212,26 +209,26 @@ jobs: key: poetry | $(System.DefaultWorkingDirectory) | ${{ parameters.service_name }}/utils/poetry.lock location: "${{ parameters.service_name }}/utils/.venv" debug: true - alias: 'Utils' + alias: "Utils" displayName: cache utils dependencies - bash: | - make install - sleep 5 + make install + sleep 5 workingDirectory: "${{ parameters.service_name }}/utils" condition: ne(variables['CacheRestored-Utils'], 'true') displayName: "Install utils " - bash: | - export out_dir="$(realpath ${{ parameters.service_name }}/dist)" - export commit_hash="$(git -C ${{ parameters.service_name }} rev-parse --short HEAD)" + export out_dir="$(realpath $service_name/dist)" + export commit_hash="$(git -C $service_name rev-parse --short HEAD)" # sha prefix is required docker gets upset if names contain -0 iirc export build_label="$(Build.BuildId)-sha${commit_hash}" export pr_number="$(PR_NUMBER)" - export service_name="${{ parameters.service_name }}" - export service_id="${{ parameters.short_service_name }}" + export service_name="$service_name" + export service_id="$service_short_name" export ANSIBLE_FORCE_COLOR=yes - make --no-print-directory -C ${{ parameters.service_name }}/utils/ansible create-build-env-vars + make --no-print-directory -C "$service_name/utils/ansible" create-build-env-vars displayName: output build env vars for artifact condition: and(succeeded(), eq(variables['build_containers'], 'true')) @@ -241,8 +238,8 @@ jobs: condition: and(succeeded(), eq(variables['build_containers'], 'true')) - bash: | - source "${{ parameters.service_name }}/dist/.build_env_vars" - account=ptl make --no-print-directory -C ${{ parameters.service_name }}/utils/ansible create-ecr-build-role + source "$service_name/dist/.build_env_vars" + account=ptl make --no-print-directory -C "$service_name/utils/ansible" create-ecr-build-role echo "##vso[task.setvariable variable=BUILD_ROLE]build-${service_id}" displayName: "ensure build role" condition: and(succeeded(), eq(variables['build_containers'], 'true')) @@ -253,9 +250,9 @@ jobs: profile: "$(BUILD_ROLE)" - bash: | - source "${{ parameters.service_name }}/dist/.build_env_vars" - CONTAINER_VARS_FILE="$(realpath ${{ parameters.service_name }}/ecs-proxies-containers.yml)" \ - make --no-print-directory -C ${{ parameters.service_name }}/utils/ansible build-ecs-proxies + source "$service_name/dist/.build_env_vars" + CONTAINER_VARS_FILE="$(realpath $service_name/ecs-proxies-containers.yml)" \ + make --no-print-directory -C "$service_name/utils/ansible" build-ecs-proxies displayName: "Build and push ECS proxies" condition: and(succeeded(), eq(variables['build_containers'], 'true')) @@ -263,7 +260,7 @@ jobs: - ${{ post_ecs_push }} - bash: | - cp ${{ parameters.service_name }}/ecs-*.yml ${{ parameters.service_name }}/dist || true + cp "$service_name"/ecs-*.yml "$service_name"/dist || true displayName: "Copy ecs-proxies-deploy configs into build artifact" condition: and(succeeded(), eq(variables['build_containers'], 'true')) @@ -288,7 +285,7 @@ jobs: displayName: "Validate manifest (template) and copy to artifact" - bash: | - cd ${{ parameters.service_name }}/utils + cd "$service_name/utils" git rev-parse HEAD > ../dist/.utils-version displayName: Snapshot utils version @@ -296,14 +293,14 @@ jobs: artifact: "$(Build.BuildNumber)" - ${{ if parameters.notify }}: - - template: '../components/update-github-status.yml' - parameters: - state: success - on_success: true - description: "Build succeeded" - - - template: '../components/update-github-status.yml' - parameters: - state: failure - on_failure: true - description: "Build failed" + - template: "../components/update-github-status.yml" + parameters: + state: success + on_success: true + description: "Build succeeded" + + - template: "../components/update-github-status.yml" + parameters: + state: failure + on_failure: true + description: "Build failed" diff --git a/azure/components/aws-assume-role.yml b/azure/components/aws-assume-role.yml index 2d5bb337..76fc3c1a 100644 --- a/azure/components/aws-assume-role.yml +++ b/azure/components/aws-assume-role.yml @@ -1,84 +1,88 @@ parameters: - - name: 'role' + - name: "role" type: string - - name: 'profile' + - name: "profile" type: string - default: '' - - name: 'aws_account' + default: "" + - name: "aws_account" type: string - default: 'ptl' + default: "ptl" steps: - - template: ./aws-clean-config.yml + - template: ./aws-clean-config.yml - - bash: | - set -e - echo "##vso[task.setvariable variable=ROLE]${{ parameters.role }}" - displayName: get role name + - bash: | + set -e + echo "##vso[task.setvariable variable=ROLE]$role" + env: + role: ${{ parameters.role }} + displayName: get role name - - bash: | - set -e + - bash: | + set -e - aws_role="$(ROLE)" - echo "assume role: '${aws_role}'" + aws_role="$(ROLE)" + echo "assume role: '${aws_role}'" - if [[ "${{ parameters.aws_account }}" =~ ^(prod|dev)$ ]]; then - echo "account is ${{ parameters.aws_account }}" - account_id="$(aws ssm get-parameter --name /account-ids/${{ parameters.aws_account }} --query Parameter.Value --output text)" - aws_role="arn:aws:iam::${account_id}:role/${aws_role}" - fi + if [[ "$AWS_ACCOUNT" =~ ^(prod|dev)$ ]]; then + echo "account is $AWS_ACCOUNT" + account_id="$(aws ssm get-parameter --name /account-ids/$AWS_ACCOUNT --query Parameter.Value --output text)" + aws_role="arn:aws:iam::${account_id}:role/${aws_role}" + fi - if [[ "${aws_role}" != arn:aws:iam:* ]]; then - echo "check if role exists" - # iam synchronisation issues can take a few to make the role appear - for i in {1..15}; do - if aws iam get-role --role-name ${aws_role} > /dev/null; then - echo role exists - sleep 2 - break - fi - echo waiting for role ... + if [[ "${aws_role}" != arn:aws:iam:* ]]; then + echo "check if role exists" + # iam synchronisation issues can take a few to make the role appear + for i in {1..15}; do + if aws iam get-role --role-name ${aws_role} > /dev/null; then + echo role exists sleep 2 - done - - account_id="$(aws sts get-caller-identity --query Account --output text)" - aws_role="arn:aws:iam::${account_id}:role/${aws_role}" - fi + break + fi + echo waiting for role ... + sleep 2 + done - cp ~/.aws/config.default ~/.aws/config - tmp_file="$(Agent.TempDirectory)/.aws.tmp.creds.json" + account_id="$(aws sts get-caller-identity --query Account --output text)" + aws_role="arn:aws:iam::${account_id}:role/${aws_role}" + fi - # add some backoff to allow for eventual consistency of IAM - for i in {2..4}; - do - if aws sts assume-role --role-arn "${aws_role}" --role-session-name build-assume-role > ${tmp_file}; then - echo assumed role - assumed_role="yes" - break - fi - let "sleep_for=$i*10"; - sleep $sleep_for - done + cp ~/.aws/config.default ~/.aws/config + tmp_file="$(Agent.TempDirectory)/.aws.tmp.creds.json" - if [[ "${assumed_role}" != "yes" ]]; then - echo "assume role failed" - exit -1 - fi + # add some backoff to allow for eventual consistency of IAM + for i in {2..4}; + do + if aws sts assume-role --role-arn "${aws_role}" --role-session-name build-assume-role > ${tmp_file}; then + echo assumed role + assumed_role="yes" + break + fi + let "sleep_for=$i*10"; + sleep $sleep_for + done - echo "aws_access_key_id = $(jq -r .Credentials.AccessKeyId ${tmp_file})" >> ~/.aws/config - echo "aws_secret_access_key = $(jq -r .Credentials.SecretAccessKey ${tmp_file})" >> ~/.aws/config - echo "aws_session_token = $(jq -r .Credentials.SessionToken ${tmp_file})" >> ~/.aws/config + if [[ "${assumed_role}" != "yes" ]]; then + echo "assume role failed" + exit -1 + fi - expiry=$(jq -r .Credentials.Expiration ${tmp_file}) - echo "##vso[task.setvariable variable=ASSUME_ROLE_EXPIRY;]$expiry" + echo "aws_access_key_id = $(jq -r .Credentials.AccessKeyId ${tmp_file})" >> ~/.aws/config + echo "aws_secret_access_key = $(jq -r .Credentials.SecretAccessKey ${tmp_file})" >> ~/.aws/config + echo "aws_session_token = $(jq -r .Credentials.SessionToken ${tmp_file})" >> ~/.aws/config - rm ${tmp_file} + expiry=$(jq -r .Credentials.Expiration ${tmp_file}) + echo "##vso[task.setvariable variable=ASSUME_ROLE_EXPIRY;]$expiry" - profile="${{ parameters.profile }}" - if [[ ! -z "${profile}" ]]; then - echo as profile ${profile} - sed -i "s#\[default\]#\[profile ${profile}\]#" ~/.aws/config - fi + rm ${tmp_file} - displayName: assume role - condition: and(succeeded(), ne(variables['ROLE'], '')) + profile="$PROFILE" + if [[ ! -z "${profile}" ]]; then + echo as profile ${profile} + sed -i "s#\[default\]#\[profile ${profile}\]#" ~/.aws/config + fi + env: + AWS_ACCOUNT: ${{ parameters.aws_account }} + PROFILE: ${{ parameters.profile }} + displayName: assume role + condition: and(succeeded(), ne(variables['ROLE'], '')) diff --git a/azure/components/cleanup-ecs-pr-proxies-job.yml b/azure/components/cleanup-ecs-pr-proxies-job.yml index a30f2f8b..f97a4b10 100644 --- a/azure/components/cleanup-ecs-pr-proxies-job.yml +++ b/azure/components/cleanup-ecs-pr-proxies-job.yml @@ -14,7 +14,7 @@ steps: displayName: Remove stale locks - bash: | - export retain_hours="${{ parameters.retain_hours }}" + export retain_hours="$retain_hours" ANSIBLE_FORCE_COLOR=yes make -C ansible remove-old-ecs-pr-deploys ERROR_CODE=$? echo ERROR_CODE - $ERROR_CODE @@ -26,6 +26,7 @@ steps: else echo "##vso[task.setvariable variable=should_retry;]false" fi - + env: + retain_hours: ${{ parameters.retain_hours }} displayName: cleanup older pr deploys condition: or(eq( ${{ parameters.retry }}, '0'), eq(variables['should_retry'], 'true')) diff --git a/azure/components/curl.yml b/azure/components/curl.yml index ccabcd6a..afa47db8 100644 --- a/azure/components/curl.yml +++ b/azure/components/curl.yml @@ -5,14 +5,18 @@ parameters: - GET - name: url - name: headers - default: '' + default: "" - name: body - default: '' + default: "" - name: display_name - default: '' - + default: "" steps: - bash: | - curl --fail -X '${{ parameters.method }}' -H '${{ parameters.headers }}' -d '${{ parameters.body }}' '${{ parameters.url }}' + curl --fail -X "$method" -H "$headers" -d "$body" "$url" + env: + method: ${{ parameters.method }} + url: ${{ parameters.url }} + headers: ${{ parameters.headers }} + body: ${{ parameters.body }} displayName: ${{ coalesce(parameters.display_name, 'curl') }} diff --git a/azure/components/get-access-token.yml b/azure/components/get-access-token.yml index 2be25fb3..c614c81d 100644 --- a/azure/components/get-access-token.yml +++ b/azure/components/get-access-token.yml @@ -11,26 +11,34 @@ parameters: default: nonprod: login.apigee.com prod: nhs-digital-prod.login.apigee.com - + steps: - bash: | set -euo pipefail - echo 'apigee_username: ${{ parameters.apigee_username }}' - echo 'apigee_organization: ${{ parameters.apigee_organization }}' - echo 'auth_url: ${{ parameters._auth_server[parameters.apigee_organization] }}' - displayName: 'Print access token debug info' + echo "apigee_username: $apigee_username" + echo "apigee_organization: $apigee_organization" + echo "auth_url: $auth_url" + env: + apigee_username: ${{ parameters.apigee_username }} + apigee_organization: ${{ parameters.apigee_organization }} + auth_url: ${{ parameters._auth_server[parameters.apigee_organization] }} + displayName: "Print access token debug info" - bash: | set -euo pipefail # fetch the access token - curl -X POST https://${{ parameters._auth_server[parameters.apigee_organization] }}/oauth/token \ + curl -X POST https://$auth_url/oauth/token \ -H "Content-Type: application/x-www-form-urlencoded" \ -H "Accept: application/json;charset=utf-8" \ -H "Authorization: Basic ZWRnZWNsaTplZGdlY2xpc2VjcmV0" \ - -d "username=${{ parameters.apigee_username }}&password=${{ parameters.apigee_password }}&mfa_token=$(secret.MFACode)&grant_type=password" | jq -e .access_token > .token + -d "username=$apigee_username&password=$apigee_password&mfa_token=$(secret.MFACode)&grant_type=password" | jq -e .access_token > .token # Set token into variable echo "##vso[task.setvariable variable=secret.AccessToken;issecret=true]`cat .token`" - displayName: 'Get Apigee Access Token' - retryCountOnTaskFailure: 5 \ No newline at end of file + env: + apigee_username: ${{ parameters.apigee_username }} + apigee_password: ${{ parameters.apigee_password }} + auth_url: ${{ parameters._auth_server[parameters.apigee_organization] }} + displayName: "Get Apigee Access Token" + retryCountOnTaskFailure: 5 From 3fe7ce191d2ad7821101383d0cbe25411c5011ef Mon Sep 17 00:00:00 2001 From: Sathiyakumar Ganesan Date: Mon, 11 May 2026 11:30:31 +0100 Subject: [PATCH 15/38] Refactor YAML scripts for improved variable handling and consistency - Updated get-mfa-code.yml to use environment variables for OTP key. - Modified set-commit-hash.yml to utilize environment variables for build source version message. - Enhanced set-facts.yml by standardizing variable references and adding environment variables for better readability. - Adjusted update-github-status.yml to streamline state reporting and use environment variables for description and environment. - Refined create-build-env-vars.yml to improve variable exports and consistency in default values. - Improved deploy-ecs-proxies-retag.yml by standardizing variable references and utilizing environment variables. - Enhanced deploy-service.yml for better readability and consistency in variable handling. - Updated setup-build-name.yml to use environment variables for service name. - Refined utils-pr-pipeline.yml for consistent quoting style and improved readability. --- azure/components/get-mfa-code.yml | 18 +- azure/components/set-commit-hash.yml | 4 +- azure/components/set-facts.yml | 28 +- azure/components/update-github-status.yml | 13 +- azure/create-build-env-vars.yml | 42 +-- azure/deploy-ecs-proxies-retag.yml | 19 +- azure/templates/deploy-service.yml | 429 +++++++++++----------- azure/templates/setup-build-name.yml | 8 +- azure/utils-pr-pipeline.yml | 19 +- 9 files changed, 305 insertions(+), 275 deletions(-) diff --git a/azure/components/get-mfa-code.yml b/azure/components/get-mfa-code.yml index d8ea34ea..008ddac6 100644 --- a/azure/components/get-mfa-code.yml +++ b/azure/components/get-mfa-code.yml @@ -3,11 +3,13 @@ parameters: type: string steps: -- bash: | - # Install requirements - set -e - - # Save code to variable - export MFA_CODE=`oathtool --totp -b '${{ parameters.apigee_otp_key }}'` - echo "##vso[task.setvariable variable=secret.MFACode;issecret=true]$MFA_CODE" - displayName: 'Get MFA Code for Apigee Login' + - bash: | + # Install requirements + set -e + + # Save code to variable + export MFA_CODE=`oathtool --totp -b "$apigee_otp_key"` + echo "##vso[task.setvariable variable=secret.MFACode;issecret=true]$MFA_CODE" + env: + apigee_otp_key: ${{ parameters.apigee_otp_key }} + displayName: "Get MFA Code for Apigee Login" diff --git a/azure/components/set-commit-hash.yml b/azure/components/set-commit-hash.yml index a289acad..6c2b36e2 100644 --- a/azure/components/set-commit-hash.yml +++ b/azure/components/set-commit-hash.yml @@ -1,4 +1,6 @@ steps: - bash: | - echo "##vso[task.setvariable variable=COMMIT_HASH]`echo $(Build.SourceVersionMessage) | cut -d' ' -f2`" + echo "##vso[task.setvariable variable=COMMIT_HASH]$(echo "$BUILD_SOURCEVERSIONMESSAGE" | cut -d' ' -f2)" + env: + BUILD_SOURCEVERSIONMESSAGE: $(Build.SourceVersionMessage) displayName: Set COMMIT_HASH diff --git a/azure/components/set-facts.yml b/azure/components/set-facts.yml index b85aec82..91dcefad 100644 --- a/azure/components/set-facts.yml +++ b/azure/components/set-facts.yml @@ -12,35 +12,41 @@ parameters: type: object - name: aws_account type: string - default: 'ptl' + default: "ptl" - name: apigee_organization type: string +env: + service_name: ${{ parameters.service_name }} + steps: - bash: | set -euo pipefail - echo "!!! Using ls -t will set the latest file for the artifact. If you get an error here, it is because '${{ parameters.service_name }}' is not the source alias name of the artifact" + echo "!!! Using ls -t will set the latest file for the artifact. If you get an error here, it is because '$service_name' is not the source alias name of the artifact" ls -R $(Pipeline.Workspace) - export SERVICE_ARTIFACT_NAME=`ls -t $(Pipeline.Workspace)/s/${{ parameters.service_name }}` + export SERVICE_ARTIFACT_NAME=`ls -t $(Pipeline.Workspace)/s/$service_name` echo "##vso[task.setvariable variable=SERVICE_ARTIFACT_NAME]$SERVICE_ARTIFACT_NAME" echo "Set Artifact Name of: $SERVICE_ARTIFACT_NAME" - displayName: 'Set SERVICE_ARTIFACT_NAME' + displayName: "Set SERVICE_ARTIFACT_NAME" - bash: | - echo "##vso[task.setvariable variable=SERVICE_DIR]$(Pipeline.Workspace)/s/${{ parameters.service_name }}/$(SERVICE_ARTIFACT_NAME)" - echo "##vso[task.setvariable variable=UTILS_DIR]$(Pipeline.Workspace)/s/${{ parameters.service_name }}/$(SERVICE_ARTIFACT_NAME)/utils" - displayName: Set SERVICE_DIR and UTILS_DIR for deploy-service + echo "##vso[task.setvariable variable=SERVICE_DIR]$(Pipeline.Workspace)/s/$service_name/$(SERVICE_ARTIFACT_NAME)" + echo "##vso[task.setvariable variable=UTILS_DIR]$(Pipeline.Workspace)/s/$service_name/$(SERVICE_ARTIFACT_NAME)/utils" + displayName: "Set SERVICE_DIR and UTILS_DIR for deploy-service" - bash: | - if [ -n "$(System.PullRequest.SourceBranch)" ]; then - export SOURCE_BRANCH="$(System.PullRequest.SourceBranch)" + if [ -n "$System_PullRequest_SourceBranch" ]; then + export SOURCE_BRANCH="$System_PullRequest_SourceBranch" else - export SOURCE_BRANCH="$(Build.SourceBranchName)" + export SOURCE_BRANCH="$Build_SourceBranchName" fi echo "##vso[task.setvariable variable=BRANCH_NAME]`echo $SOURCE_BRANCH | sed -r 's/[/|\\@":<>?*]+/-/g'`" + env: + System_PullRequest_SourceBranch: $(System.PullRequest.SourceBranch) + Build_SourceBranchName: $(Build.SourceBranchName) displayName: Set and replace invalid characters in branch name - - bash: 'echo "##vso[build.updatebuildnumber]${{ parameters.service_name }}-$(BRANCH_NAME)+$(Build.BuildID)"' + - bash: 'echo "##vso[build.updatebuildnumber]$service_name-$(BRANCH_NAME)+$(Build.BuildID)"' condition: eq(variables['Build.SourceBranchName'], 'merge') displayName: Update build ID if this is a pull request diff --git a/azure/components/update-github-status.yml b/azure/components/update-github-status.yml index ec93d770..9d77e925 100644 --- a/azure/components/update-github-status.yml +++ b/azure/components/update-github-status.yml @@ -3,10 +3,10 @@ parameters: displayName: State type: string values: - - pending # A build is underway - - success # A build succeeded - - failure # A build failed in an expected way, e.g. the tests failed - - error # A build finishes for an unexpected reason, e.g. error in bash step, bad config + - pending # A build is underway + - success # A build succeeded + - failure # A build failed in an expected way, e.g. the tests failed + - error # A build finishes for an unexpected reason, e.g. error in bash step, bad config - name: description displayName: Description type: string @@ -36,11 +36,14 @@ steps: if [[ "$(NOTIFY_COMMIT_SHA)" =~ ^[0-9a-f]+$ ]]; then echo "Reporting state ${{ parameters.state }} to ${NOTIFY_GITHUB_REPOSITORY}" echo "##[debug] Hitting https://api.github.com/repos/${NOTIFY_GITHUB_REPOSITORY}/statuses/$(NOTIFY_COMMIT_SHA)" - curl --fail -q -X POST "https://api.github.com/repos/${NOTIFY_GITHUB_REPOSITORY}/statuses/$(NOTIFY_COMMIT_SHA)" -d '{"state": "${{ parameters.state }}", "context": "$(Build.DefinitionName) ${{ parameters.environment }}", "description": "${{ parameters.description }}", "target_url": "https://dev.azure.com/NHSD-APIM/API Platform/_build/results?buildId=$(Build.BuildID)"}' --user $(GITHUB_USER):$(GITHUB_ACCESS_TOKEN) + curl --fail -q -X POST "https://api.github.com/repos/${NOTIFY_GITHUB_REPOSITORY}/statuses/$(NOTIFY_COMMIT_SHA)" -d '{"state": "${{ parameters.state }}", "context": "$(Build.DefinitionName) $environment", "description": "$description", "target_url": "https://dev.azure.com/NHSD-APIM/API Platform/_build/results?buildId=$(Build.BuildID)"}' --user $(GITHUB_USER):$(GITHUB_ACCESS_TOKEN) else echo "##[warning]$(NOTIFY_COMMIT_SHA) doesn't look like a commit hash" echo "##[task.logissue type=warning]Task wasn't able to find the right commit sha value to notify github." fi + env: + description: ${{ parameters.description }} + environment: ${{ parameters.environment }} displayName: "Notify GitHub: ${{ parameters.state }}" condition: | or( diff --git a/azure/create-build-env-vars.yml b/azure/create-build-env-vars.yml index 740df5c1..4ccc4cac 100644 --- a/azure/create-build-env-vars.yml +++ b/azure/create-build-env-vars.yml @@ -1,34 +1,34 @@ - parameters: - - name: 'out_dir' + - name: "out_dir" type: string - default: './' - - name: 'source_root' + default: "./" + - name: "source_root" type: string - default: './' - - name: 'service_name' + default: "./" + - name: "service_name" type: string - default: '' - - name: 'service_id' + default: "" + - name: "service_id" type: string - default: '' - - name: 'utils_dir' + default: "" + - name: "utils_dir" type: string - default: 'utils' + default: "utils" steps: - - bash: | - export out_dir="$(realpath ${{ parameters.out_dir }})" - export commit_hash="$(git -C ${{ parameters.source_root }} rev-parse --short HEAD)" + export out_dir="$(realpath $out_dir)" + export commit_hash="$(git -C $source_root rev-parse --short HEAD)" # sha prefix is required docker gets upset if names contain -0 iirc export build_label="$(Build.BuildId)-sha${commit_hash}" export pr_number="$(System.PullRequest.PullRequestNumber)" - export service_name="${{ parameters.service_name }}" - export service_id="${{ parameters.service_id }}" + export service_name="$service_name" + export service_id="$service_id" export ANSIBLE_FORCE_COLOR=yes - make --no-print-directory -C ${{ parameters.utils_dir }}/ansible create-build-env-vars - + make --no-print-directory -C $utils_dir/ansible create-build-env-vars + env: + out_dir: ${{ parameters.out_dir }} + source_root: ${{ parameters.source_root }} + service_name: ${{ parameters.service_name }} + service_id: ${{ parameters.service_id }} + utils_dir: ${{ parameters.utils_dir }} displayName: output build env vars for artifact - - - diff --git a/azure/deploy-ecs-proxies-retag.yml b/azure/deploy-ecs-proxies-retag.yml index 02679c72..06604a92 100644 --- a/azure/deploy-ecs-proxies-retag.yml +++ b/azure/deploy-ecs-proxies-retag.yml @@ -1,22 +1,25 @@ parameters: - - name: 'container_vars' + - name: "container_vars" type: string - default: 'ecs-proxies-containers.yml' + default: "ecs-proxies-containers.yml" - - name: 'env_vars_dir' + - name: "env_vars_dir" type: string - default: './' + default: "./" - - name: 'utils_dir' + - name: "utils_dir" type: string - default: 'utils' + default: "utils" steps: - bash: | set -e - source "${{ parameters.env_vars_dir }}/.build_env_vars" + source "$env_vars_dir/.build_env_vars" export CONTAINER_VARS_FILE="$SERVICE_DIR/ecs-proxies-containers.yml - make --no-print-directory -C ${{ parameters.utils_dir }}/ansible deploy-ecs-proxies-retag + make --no-print-directory -C $utils_dir/ansible deploy-ecs-proxies-retag + env: + env_vars_dir: ${{ parameters.env_vars_dir }} + utils_dir: ${{ parameters.utils_dir }} displayName: "Retag ECS proxies" diff --git a/azure/templates/deploy-service.yml b/azure/templates/deploy-service.yml index 45ae358e..f8f9bf2a 100644 --- a/azure/templates/deploy-service.yml +++ b/azure/templates/deploy-service.yml @@ -34,7 +34,7 @@ parameters: type: stepList - name: spec_file - name: friendly_api_name - default: '' + default: "" - name: portal_api_requires_callback_url - name: make_spec_visible # To mark a pipeline variable as deprecated, go to the @@ -49,19 +49,28 @@ parameters: - portal_api_requires_callback_url - spec_file +env: + service_name: ${{ parameters.service_name }} + apigee_environment: ${{ parameters.apigee_environment }} + aws_account: ${{ parameters.aws_account }} + service_base_path: ${{ parameters.service_base_path }} + fully_qualified_service_name: ${{ parameters.fully_qualified_service_name }} + apigee_organization: ${{ parameters.apigee_organization }} + proxy_path: ${{ parameters.proxy_path }} + steps: # Warn user they are passing deprecated variables to the deployment pipeline - ${{ each variable_name in parameters._deprecated_pipeline_variables }}: - - ${{ if ne(parameters[variable_name], 'DEPRECATED') }}: - - bash: | - echo "##[warning]Pipeline parameter ${{ variable_name }} is deprecated" - echo "##[warning]The value that appears in Apigee is derived from your repository's manifest.yml/manifest_template.yml file." - echo "##[warning]To remove this warning stop passing the variable into the pipeline parameters." - echo "##vso[task.complete result=SucceededWithIssues;]DONE" - displayName: Warn ${{ variable_name }} is a deprecated pipeline parameter + - ${{ if ne(parameters[variable_name], 'DEPRECATED') }}: + - bash: | + echo "##[warning]Pipeline parameter ${{ variable_name }} is deprecated" + echo "##[warning]The value that appears in Apigee is derived from your repository's manifest.yml/manifest_template.yml file." + echo "##[warning]To remove this warning stop passing the variable into the pipeline parameters." + echo "##vso[task.complete result=SucceededWithIssues;]DONE" + displayName: Warn ${{ variable_name }} is a deprecated pipeline parameter - bash: | ls -R - if [[ -f ecs-deploy-${{ parameters.apigee_environment }}.yml ]] || [[ -f ecs-proxies-deploy.yml ]] || [[ -f ecs-proxies-deploy-sandbox.yml ]]; then + if [[ -f ecs-deploy-${apigee_environment}.yml ]] || [[ -f ecs-proxies-deploy.yml ]] || [[ -f ecs-proxies-deploy-sandbox.yml ]]; then echo "##vso[task.setvariable variable=deploy_containers]true" else echo "##vso[task.setvariable variable=deploy_containers]false" @@ -74,7 +83,7 @@ steps: key: poetry | utils | $(UTILS_DIR)/poetry.lock location: "$(UTILS_DIR)/.venv" debug: true - alias: 'UtilsPreReq' + alias: "UtilsPreReq" displayName: cache utils pre-requisites - bash: | @@ -94,79 +103,82 @@ steps: condition: and(succeeded(), eq(variables['deploy_containers'], 'true')) - ${{ if parameters.proxy_path }}: - - bash: | - echo "Deploying ${{ parameters.service_name }} artifact $(SERVICE_ARTIFACT_NAME) as ${{ parameters.fully_qualified_service_name }} to ${{ parameters.service_base_path }} on $(ENV_URL)" - displayName: 'Deploy Info' - - - ${{ each pre_template_step in parameters.pre_template }}: - - ${{ pre_template_step }} - - # pre_template steps might have been doing cross account stuff - # make sure we bring everything back to the correct AWS role here - - template: ../components/aws-assume-role.yml - parameters: - role: "auto-ops" - profile: "apm_${{ parameters.aws_account }}" - aws_account: "${{ parameters.aws_account }}" - - - ${{ if parameters.jinja_templates }}: - - bash: mkdir -p group_vars/all && touch jinja_templates.yml - workingDirectory: "$(UTILS_DIR)/ansible/" - displayName: Prepare extra template-proxy vars - - - ${{ each jinja_template in parameters.jinja_templates }}: - - bash: 'echo "${{ jinja_template.key }}: ${{ jinja_template.value }}" >> jinja_templates.yml' - workingDirectory: "$(UTILS_DIR)/ansible/group_vars/all" - displayName: "Set ${{ jinja_template.key }} to ${{ jinja_template.value }}" - - - bash: | - set -euo pipefail - - export SERVICE_NAME="${{ parameters.service_name }}" - export PROXIES_DIR="$(SERVICE_DIR)/proxies" - export SERVICE_BASE_PATH="${{ parameters.service_base_path }}" - export APIGEE_ENVIRONMENT="${{ parameters.apigee_environment }}" - export HOSTED_TARGET_CONNECTION_PATH_SUFFIX="${{ parameters.hosted_target_connection_path_suffix }}" - export HOSTED_TARGET_HEALTHCHECK_SUFFIX="${{ parameters.hosted_target_healthcheck_suffix }}" - export SOURCE_COMMIT_ID=$(Build.SourceVersion) - export RELEASE_RELEASEID=$(Build.BuildId) - - export ASSUMED_VERSION=`echo $SERVICE_ARTIFACT_NAME | ( grep -E -o "v[0-9]+\.[0-9]+\.[0-9]+-?[a-z]*" || true ) | tail -1` - if [[ ! -z $ASSUMED_VERSION ]]; then - export DEPLOYED_VERSION=$ASSUMED_VERSION - else - export DEPLOYED_VERSION="${{ parameters.fully_qualified_service_name }}" - fi - - if [ -f $(SERVICE_DIR)/.build_env_vars ]; then - source $(SERVICE_DIR)/.build_env_vars - fi - - cd $(UTILS_DIR) - ANSIBLE_FORCE_COLOR=yes \ - make --no-print-directory -C ansible template-proxies - - if [ -f $(SERVICE_DIR)/manifest.yml ]; then - DIST_DIR=$(SERVICE_DIR) \ - PROXY_DIR=${{ parameters.proxy_path }} \ - ANSIBLE_FORCE_COLOR=yes \ - make --no-print-directory -C ansible add-apim-guids-policy - fi - displayName: Template proxies + - bash: | + echo "Deploying $service_name artifact $(SERVICE_ARTIFACT_NAME) as $fully_qualified_service_name to $service_base_path on $(ENV_URL)" + displayName: "Deploy Info" + + - ${{ each pre_template_step in parameters.pre_template }}: + - ${{ pre_template_step }} + + # pre_template steps might have been doing cross account stuff + # make sure we bring everything back to the correct AWS role here + - template: ../components/aws-assume-role.yml + parameters: + role: "auto-ops" + profile: "apm_${{ parameters.aws_account }}" + aws_account: "${{ parameters.aws_account }}" + + - ${{ if parameters.jinja_templates }}: + - bash: mkdir -p group_vars/all && touch jinja_templates.yml + workingDirectory: "$(UTILS_DIR)/ansible/" + displayName: Prepare extra template-proxy vars + + - ${{ each jinja_template in parameters.jinja_templates }}: + - bash: 'echo "${{ jinja_template.key }}: ${{ jinja_template.value }}" >> jinja_templates.yml' + workingDirectory: "$(UTILS_DIR)/ansible/group_vars/all" + displayName: "Set ${{ jinja_template.key }} to ${{ jinja_template.value }}" - - ${{ each post_template_step in parameters.post_template }}: - - ${{ post_template_step }} + - bash: | + set -euo pipefail + + export SERVICE_NAME="$service_name" + export PROXIES_DIR="$(SERVICE_DIR)/proxies" + export SERVICE_BASE_PATH="$service_base_path" + export APIGEE_ENVIRONMENT="$apigee_environment" + export HOSTED_TARGET_CONNECTION_PATH_SUFFIX="$hosted_target_connection_path_suffix" + export HOSTED_TARGET_HEALTHCHECK_SUFFIX="$hosted_target_healthcheck_suffix" + export SOURCE_COMMIT_ID=$(Build.SourceVersion) + export RELEASE_RELEASEID=$(Build.BuildId) + + export ASSUMED_VERSION=`echo $SERVICE_ARTIFACT_NAME | ( grep -E -o "v[0-9]+\.[0-9]+\.[0-9]+-?[a-z]*" || true ) | tail -1` + if [[ ! -z $ASSUMED_VERSION ]]; then + export DEPLOYED_VERSION=$ASSUMED_VERSION + else + export DEPLOYED_VERSION="$fully_qualified_service_name" + fi + + if [ -f $(SERVICE_DIR)/.build_env_vars ]; then + source $(SERVICE_DIR)/.build_env_vars + fi + + cd $(UTILS_DIR) + ANSIBLE_FORCE_COLOR=yes \ + make --no-print-directory -C ansible template-proxies + + if [ -f $(SERVICE_DIR)/manifest.yml ]; then + DIST_DIR=$(SERVICE_DIR) \ + PROXY_DIR=$proxy_path \ + ANSIBLE_FORCE_COLOR=yes \ + make --no-print-directory -C ansible add-apim-guids-policy + fi + env: + hosted_target_connection_path_suffix: ${{ parameters.hosted_target_connection_path_suffix }} + hosted_target_healthcheck_suffix: ${{ parameters.hosted_target_healthcheck_suffix }} + displayName: Template proxies + + - ${{ each post_template_step in parameters.post_template }}: + - ${{ post_template_step }} - ${{ each pre_deploy_step in parameters.pre_deploy }}: - - ${{ pre_deploy_step }} + - ${{ pre_deploy_step }} - bash: | set -e proxy_vars_file="" - if [ -f $(SERVICE_DIR)/ecs-deploy-${{ parameters.apigee_environment }}.yml ]; then - proxy_vars_file="$(SERVICE_DIR)/ecs-deploy-${{ parameters.apigee_environment }}.yml" + if [ -f $(SERVICE_DIR)/ecs-deploy-${apigee_environment}.yml ]; then + proxy_vars_file="$(SERVICE_DIR)/ecs-deploy-${apigee_environment}.yml" else if [ -f $(SERVICE_DIR)/ecs-deploy-all.yml ]; then proxy_vars_file="$(SERVICE_DIR)/ecs-deploy-all.yml" @@ -176,39 +188,37 @@ steps: if [[ ! -z "${proxy_vars_file}" ]]; then source $(SERVICE_DIR)/.build_env_vars - deploy_role="deploy-${{ parameters.apigee_environment }}-${service_id}" + deploy_role="deploy-${apigee_environment}-${service_id}" - account=${{ parameters.aws_account }} \ - SERVICE_BASE_PATH=${{ parameters.service_base_path }} \ - APIGEE_ENVIRONMENT=${{ parameters.apigee_environment }} \ + account=${aws_account} \ + SERVICE_BASE_PATH=${service_base_path} \ + APIGEE_ENVIRONMENT=${apigee_environment} \ make --no-print-directory -C $(UTILS_DIR)/ansible create-api-deployment-pre-reqs fi echo "##vso[task.setvariable variable=PROXY_VARS_FILE]${proxy_vars_file}" echo "##vso[task.setvariable variable=DEPLOY_ROLE]${deploy_role}" - displayName: Create ECS Prerequisites condition: and(succeeded(), eq(variables['deploy_containers'], 'true')) - bash: | - echo "##vso[task.setvariable variable=SERVICE_BASE_PATH_FOR_CONDITION]${{ parameters.service_base_path }}" + echo "##vso[task.setvariable variable=SERVICE_BASE_PATH_FOR_CONDITION]$service_base_path" displayName: Set SERVICE_BASE_PATH_FOR_CONDITION - - bash: | set -e export CONTAINER_VARS_FILE="$SERVICE_DIR/ecs-proxies-containers.yml" - + source $(SERVICE_DIR)/.build_env_vars - export SERVICE_BASE_PATH="${{ parameters.service_base_path }}" + export SERVICE_BASE_PATH="$service_base_path" echo "SERVICE_BASE_PATH='${SERVICE_BASE_PATH}'" export ASSUMED_VERSION=$(echo $SERVICE_ARTIFACT_NAME | grep -E -o "v[0-9]+\.[0-9]+\.[0-9]+-?[a-z]*" || true | tail -1) - export DEPLOYED_VERSION=${ASSUMED_VERSION:-${{ parameters.fully_qualified_service_name }}} + export DEPLOYED_VERSION=${ASSUMED_VERSION:-$fully_qualified_service_name} - account=${{ parameters.aws_account }} \ + account=$aws_account \ CONTAINER_VARS_FILE="${CONTAINER_VARS_FILE}" \ SOURCE_COMMIT_ID="$(Build.SourceVersion)" \ RELEASE_RELEASEID="$(Build.BuildId)" \ @@ -217,14 +227,12 @@ steps: displayName: Retag ECS image condition: and(succeeded(), ne(variables['DEPLOY_ROLE'], ''), contains(variables['SERVICE_BASE_PATH_FOR_CONDITION'], 'canary'), not(contains(variables['SERVICE_BASE_PATH_FOR_CONDITION'], '-pr-'))) - - - template: ../components/aws-assume-role.yml parameters: role: "$(DEPLOY_ROLE)" profile: "$(DEPLOY_ROLE)" aws_account: "${{ parameters.aws_account }}" - + - bash: | set -e proxy_vars_file="$(PROXY_VARS_FILE)" @@ -235,10 +243,10 @@ steps: if [[ ! -z $ASSUMED_VERSION ]]; then export DEPLOYED_VERSION=$ASSUMED_VERSION else - export DEPLOYED_VERSION="${{ parameters.fully_qualified_service_name }}" + export DEPLOYED_VERSION="$fully_qualified_service_name" fi - - + + if [[ "$SERVICE_BASE_PATH_FOR_CONDITION" == *"-pr-"* ]]; then export TF_VAR_use_ecs_tag=false else @@ -250,12 +258,12 @@ steps: export use_ecs_tag=$TF_VAR_use_ecs_tag - account=${{ parameters.aws_account }} \ + account=$aws_account \ PROXY_VARS_FILE="${proxy_vars_file}" \ SOURCE_COMMIT_ID="$(Build.SourceVersion)" \ RELEASE_RELEASEID="$(Build.BuildId)" \ - SERVICE_BASE_PATH=${{ parameters.service_base_path }} \ - APIGEE_ENVIRONMENT=${{ parameters.apigee_environment }} \ + SERVICE_BASE_PATH=$service_base_path \ + APIGEE_ENVIRONMENT=$apigee_environment \ use_ecs_tag="${use_ecs_tag}" \ make --no-print-directory -C $(UTILS_DIR)/ansible deploy-ecs-proxies @@ -263,148 +271,151 @@ steps: condition: and(succeeded(), ne(variables['DEPLOY_ROLE'], '')) - ${{ if parameters.proxy_path }}: - - bash: | - set -euo pipefail - - export PROXY_DIR="$(SERVICE_DIR)/proxies/${{ parameters.proxy_path }}" - export SERVICE_NAME="${{ parameters.service_name }}" - export FULLY_QUALIFIED_SERVICE_NAME="${{ parameters.fully_qualified_service_name }}" - export SERVICE_BASE_PATH="${{ parameters.service_base_path }}" - export APIGEE_ACCESS_TOKEN="$(secret.AccessToken)" - export APIGEE_ENVIRONMENT="${{ parameters.apigee_environment }}" - export APIGEE_ORGANIZATION="nhsd-${{ parameters.apigee_organization }}" - export PING="${{ parameters.ping }}" - export ANSIBLE_FORCE_COLOR=yes - - make --no-print-directory -C $(UTILS_DIR)/ansible deploy-apigee-proxy - displayName: Deploy proxy + - bash: | + set -euo pipefail + + export PROXY_DIR="$(SERVICE_DIR)/proxies/$proxy_path" + export SERVICE_NAME="$service_name" + export FULLY_QUALIFIED_SERVICE_NAME="$fully_qualified_service_name" + export SERVICE_BASE_PATH="$service_base_path" + export APIGEE_ACCESS_TOKEN="$(secret.AccessToken)" + export APIGEE_ENVIRONMENT="$apigee_environment" + export APIGEE_ORGANIZATION="nhsd-$apigee_organization" + export PING="$ping" + export ANSIBLE_FORCE_COLOR=yes + + make --no-print-directory -C $(UTILS_DIR)/ansible deploy-apigee-proxy + env: + ping: ${{ parameters.ping }} + displayName: Deploy proxy - bash: | set -euo pipefail export APIGEE_ACCESS_TOKEN="$(secret.AccessToken)" - export APIGEE_ENVIRONMENT="${{ parameters.apigee_environment }}" - export APIGEE_ORGANIZATION="nhsd-${{ parameters.apigee_organization }}" + export APIGEE_ENVIRONMENT="$apigee_environment" + export APIGEE_ORGANIZATION="nhsd-$apigee_organization" export ANSIBLE_FORCE_COLOR=yes export DIST_DIR=$(SERVICE_DIR) - export PULL_REQUEST="${{ parameters.pr_label }}" + export PULL_REQUEST="$pr_label" make --no-print-directory -C $(UTILS_DIR)/ansible deploy-manifest - displayName: 'Deploy Manifest' + env: + pr_label: ${{ parameters.pr_label }} + displayName: "Deploy Manifest" - ${{ if parameters.proxy_path }}: - - bash: | - set -euo pipefail - - echo "Setting stage name as snake case for monitoring" - export MONITORING_STAGE_NAME="${{ replace(parameters.stage_name, '_', '-') }}" - echo "MONITORING_STAGE_NAME=${MONITORING_STAGE_NAME}" - echo "##vso[task.setvariable variable=MONITORING_STAGE_NAME]$MONITORING_STAGE_NAME" - - if [[ "${{ parameters.apigee_environment }}" == "prod" ]]; then - export url="https://api.service.nhs.uk/monitoring-sd/service" - export status_body='{ "${{ parameters.service_name }}": { "${{ parameters.apigee_environment }}": [ "${{ parameters.service_name }}@$(MONITORING_STAGE_NAME)=http_2xx_with_api_key https://api.service.nhs.uk/${{ parameters.service_base_path }}/_status" ] } }' - export ping_body='{ "${{ parameters.service_name }}": { "${{ parameters.apigee_environment }}": [ "${{ parameters.service_name }}@$(MONITORING_STAGE_NAME)=http_2xx https://api.service.nhs.uk/${{ parameters.service_base_path }}/_ping" ] } }' - else - export url="https://internal-dev.api.service.nhs.uk/monitoring-sd/service" - export status_body='{ "${{ parameters.service_name }}": { "${{ parameters.apigee_environment }}": [ "${{ parameters.service_name }}@$(MONITORING_STAGE_NAME)=http_2xx_with_api_key https://${{ parameters.apigee_environment }}.api.service.nhs.uk/${{ parameters.service_base_path }}/_status" ] } }' - export ping_body='{ "${{ parameters.service_name }}": { "${{ parameters.apigee_environment }}": [ "${{ parameters.service_name }}@$(MONITORING_STAGE_NAME)=http_2xx https://${{ parameters.apigee_environment }}.api.service.nhs.uk/${{ parameters.service_base_path }}/_ping" ] } }' - fi - - echo "##vso[task.setvariable variable=is_pull_request]false" - echo "##vso[task.setvariable variable=check_and_enable_ping]${{ parameters.enable_monitoring }}" - echo "##vso[task.setvariable variable=check_and_enable_status]${{ parameters.enable_status_monitoring }}" - - if [[ "${{ parameters.service_base_path }}" == *"-pr-"* ]]; then - echo "##vso[task.setvariable variable=check_and_enable_ping]false" - echo "##vso[task.setvariable variable=check_and_enable_status]false" - echo "##vso[task.setvariable variable=is_pull_request]true" - fi - - echo "##vso[task.setvariable variable=url]$url" - echo "##vso[task.setvariable variable=status_body]$status_body" - echo "##vso[task.setvariable variable=ping_body]$ping_body" - displayName: 'Set monitoring variables' + - bash: | + set -euo pipefail + + echo "Setting stage name as snake case for monitoring" + export MONITORING_STAGE_NAME="${{ replace(parameters.stage_name, '_', '-') }}" + echo "MONITORING_STAGE_NAME=${MONITORING_STAGE_NAME}" + echo "##vso[task.setvariable variable=MONITORING_STAGE_NAME]$MONITORING_STAGE_NAME" + + if [[ "${{ parameters.apigee_environment }}" == "prod" ]]; then + export url="https://api.service.nhs.uk/monitoring-sd/service" + export status_body='{ "${{ parameters.service_name }}": { "${{ parameters.apigee_environment }}": [ "${{ parameters.service_name }}@$(MONITORING_STAGE_NAME)=http_2xx_with_api_key https://api.service.nhs.uk/${{ parameters.service_base_path }}/_status" ] } }' + export ping_body='{ "${{ parameters.service_name }}": { "${{ parameters.apigee_environment }}": [ "${{ parameters.service_name }}@$(MONITORING_STAGE_NAME)=http_2xx https://api.service.nhs.uk/${{ parameters.service_base_path }}/_ping" ] } }' + else + export url="https://internal-dev.api.service.nhs.uk/monitoring-sd/service" + export status_body='{ "${{ parameters.service_name }}": { "${{ parameters.apigee_environment }}": [ "${{ parameters.service_name }}@$(MONITORING_STAGE_NAME)=http_2xx_with_api_key https://${{ parameters.apigee_environment }}.api.service.nhs.uk/${{ parameters.service_base_path }}/_status" ] } }' + export ping_body='{ "${{ parameters.service_name }}": { "${{ parameters.apigee_environment }}": [ "${{ parameters.service_name }}@$(MONITORING_STAGE_NAME)=http_2xx https://${{ parameters.apigee_environment }}.api.service.nhs.uk/${{ parameters.service_base_path }}/_ping" ] } }' + fi + + echo "##vso[task.setvariable variable=is_pull_request]false" + echo "##vso[task.setvariable variable=check_and_enable_ping]${{ parameters.enable_monitoring }}" + echo "##vso[task.setvariable variable=check_and_enable_status]${{ parameters.enable_status_monitoring }}" + + if [[ "${{ parameters.service_base_path }}" == *"-pr-"* ]]; then + echo "##vso[task.setvariable variable=check_and_enable_ping]false" + echo "##vso[task.setvariable variable=check_and_enable_status]false" + echo "##vso[task.setvariable variable=is_pull_request]true" + fi + + echo "##vso[task.setvariable variable=url]$url" + echo "##vso[task.setvariable variable=status_body]$status_body" + echo "##vso[task.setvariable variable=ping_body]$ping_body" + displayName: "Set monitoring variables" - - bash: | - set -euo pipefail + - bash: | + set -euo pipefail - if [[ "${{ parameters.apigee_environment }}" == "prod" ]]; then - export ping_endpoint_response=`curl -s -o /dev/null -w '%{http_code}' -H "apikey: $(status-endpoint-api-key)" https://api.service.nhs.uk/${{ parameters.service_base_path }}/_ping` - else - export ping_endpoint_response=`curl -s -o /dev/null -w '%{http_code}' -H "apikey: $(status-endpoint-api-key)" https://${{ parameters.apigee_environment }}.api.service.nhs.uk/${{ parameters.service_base_path }}/_ping` - fi + if [[ "${{ parameters.apigee_environment }}" == "prod" ]]; then + export ping_endpoint_response=`curl -s -o /dev/null -w '%{http_code}' -H "apikey: $(status-endpoint-api-key)" https://api.service.nhs.uk/${{ parameters.service_base_path }}/_ping` + else + export ping_endpoint_response=`curl -s -o /dev/null -w '%{http_code}' -H "apikey: $(status-endpoint-api-key)" https://${{ parameters.apigee_environment }}.api.service.nhs.uk/${{ parameters.service_base_path }}/_ping` + fi - echo _ping_response=$ping_endpoint_response - echo "##vso[task.setvariable variable=ping_endpoint_response]$ping_endpoint_response" + echo _ping_response=$ping_endpoint_response + echo "##vso[task.setvariable variable=ping_endpoint_response]$ping_endpoint_response" - if [[ $ping_endpoint_response == "404" ]]; then - echo "##vso[task.logissue type=error]Your proxy doesn't have a _ping endpoint therefore we can't monitor this proxy and it should not be released, use the flag 'enable_monitoring=false' if your API doesn't support _ping healthcheck." - exit 1 - fi + if [[ $ping_endpoint_response == "404" ]]; then + echo "##vso[task.logissue type=error]Your proxy doesn't have a _ping endpoint therefore we can't monitor this proxy and it should not be released, use the flag 'enable_monitoring=false' if your API doesn't support _ping healthcheck." + exit 1 + fi - if [[ $ping_endpoint_response != "200" ]]; then - echo "##vso[task.logissue type=error]Your proxy's _ping endpoint has encounted an error and should not be released until it is resolved." - exit 1 - fi - displayName: 'Check _ping' - condition: and(succeeded(), eq(variables['check_and_enable_ping'], 'true')) + if [[ $ping_endpoint_response != "200" ]]; then + echo "##vso[task.logissue type=error]Your proxy's _ping endpoint has encounted an error and should not be released until it is resolved." + exit 1 + fi + displayName: "Check _ping" + condition: and(succeeded(), eq(variables['check_and_enable_ping'], 'true')) - - bash: | - set -euo pipefail + - bash: | + set -euo pipefail - if [[ "${{ parameters.apigee_environment }}" == "prod" ]]; then - export status_endpoint_response=`curl -s -o /dev/null -w '%{http_code}' -H "apikey: $(status-endpoint-api-key)" https://api.service.nhs.uk/${{ parameters.service_base_path }}/_status` - else - export status_endpoint_response=`curl -s -o /dev/null -w '%{http_code}' -H "apikey: $(status-endpoint-api-key)" https://${{ parameters.apigee_environment }}.api.service.nhs.uk/${{ parameters.service_base_path }}/_status` - fi + if [[ "${{ parameters.apigee_environment }}" == "prod" ]]; then + export status_endpoint_response=`curl -s -o /dev/null -w '%{http_code}' -H "apikey: $(status-endpoint-api-key)" https://api.service.nhs.uk/${{ parameters.service_base_path }}/_status` + else + export status_endpoint_response=`curl -s -o /dev/null -w '%{http_code}' -H "apikey: $(status-endpoint-api-key)" https://${{ parameters.apigee_environment }}.api.service.nhs.uk/${{ parameters.service_base_path }}/_status` + fi - echo _status_response=$status_endpoint_response - echo "##vso[task.setvariable variable=status_endpoint_response]$status_endpoint_response" + echo _status_response=$status_endpoint_response + echo "##vso[task.setvariable variable=status_endpoint_response]$status_endpoint_response" - if [[ $status_endpoint_response == "404" ]]; then - echo "##vso[task.logissue type=error]Your proxy doesn't have a _status endpoint therefore we can't monitor this proxy and it should not be released, use the flag 'enable_status_monitoring=false' if your API doesn't support _status healthcheck." - exit 1 - fi - - if [[ $status_endpoint_response != "200" ]]; then - echo "##vso[task.logissue type=error]Your proxy's _status endpoint has encounted an error and should not be released until it is resolved." - exit 1 - fi - displayName: 'Check _status' - condition: and(succeeded(), eq(variables['check_and_enable_status'], 'true')) + if [[ $status_endpoint_response == "404" ]]; then + echo "##vso[task.logissue type=error]Your proxy doesn't have a _status endpoint therefore we can't monitor this proxy and it should not be released, use the flag 'enable_status_monitoring=false' if your API doesn't support _status healthcheck." + exit 1 + fi - - bash: | - set -euo pipefail + if [[ $status_endpoint_response != "200" ]]; then + echo "##vso[task.logissue type=error]Your proxy's _status endpoint has encounted an error and should not be released until it is resolved." + exit 1 + fi + displayName: "Check _status" + condition: and(succeeded(), eq(variables['check_and_enable_status'], 'true')) - curl --fail -X 'POST' -H 'apikey: $(MONITORING_API_KEY)' -d '$(status_body)' $(url) - displayName: Enable _status monitoring - condition: and(succeeded(), eq(variables['check_and_enable_status'], 'true')) + - bash: | + set -euo pipefail - - bash: | - set -euo pipefail + curl --fail -X 'POST' -H 'apikey: $(MONITORING_API_KEY)' -d '$(status_body)' $(url) + displayName: Enable _status monitoring + condition: and(succeeded(), eq(variables['check_and_enable_status'], 'true')) - curl --fail -X 'DELETE' -H 'apikey: $(MONITORING_API_KEY)' -d '$(status_body)' $(url) - echo - echo "_status endpoint monitoring is disabled" - displayName: Disable _status monitoring - condition: and(succeeded(), eq(variables['check_and_enable_status'], 'false'), eq(variables['is_pull_request'], 'false')) + - bash: | + set -euo pipefail + curl --fail -X 'DELETE' -H 'apikey: $(MONITORING_API_KEY)' -d '$(status_body)' $(url) + echo + echo "_status endpoint monitoring is disabled" + displayName: Disable _status monitoring + condition: and(succeeded(), eq(variables['check_and_enable_status'], 'false'), eq(variables['is_pull_request'], 'false')) - - bash: | - set -euo pipefail + - bash: | + set -euo pipefail - curl --fail -X 'POST' -H 'apikey: $(MONITORING_API_KEY)' -d '$(ping_body)' $(url) - displayName: Enable _ping monitoring - condition: and(succeeded(), eq(variables['check_and_enable_ping'], 'true')) + curl --fail -X 'POST' -H 'apikey: $(MONITORING_API_KEY)' -d '$(ping_body)' $(url) + displayName: Enable _ping monitoring + condition: and(succeeded(), eq(variables['check_and_enable_ping'], 'true')) - - bash: | - set -euo pipefail + - bash: | + set -euo pipefail - curl --fail -X 'DELETE' -H 'apikey: $(MONITORING_API_KEY)' -d '$(ping_body)' $(url) - echo - echo "_ping endpoint monitoring is disabled" - displayName: Disable _ping monitoring - condition: and(succeeded(), eq(variables['check_and_enable_ping'], 'false'), eq(variables['is_pull_request'], 'false')) + curl --fail -X 'DELETE' -H 'apikey: $(MONITORING_API_KEY)' -d '$(ping_body)' $(url) + echo + echo "_ping endpoint monitoring is disabled" + displayName: Disable _ping monitoring + condition: and(succeeded(), eq(variables['check_and_enable_ping'], 'false'), eq(variables['is_pull_request'], 'false')) - ${{ each post_deploy_step in parameters.post_deploy }}: - - ${{ post_deploy_step }} + - ${{ post_deploy_step }} diff --git a/azure/templates/setup-build-name.yml b/azure/templates/setup-build-name.yml index 0d54059a..07258990 100644 --- a/azure/templates/setup-build-name.yml +++ b/azure/templates/setup-build-name.yml @@ -8,11 +8,15 @@ steps: if [ -n "$(System.PullRequest.SourceBranch)" ]; then export SOURCE_BRANCH="$(System.PullRequest.SourceBranch)" else - export SOURCE_BRANCH=`echo $(SERVICE_ARTIFACT_NAME) | cut -d'+' -f1 | sed -e "s/${{ parameters.service_name }}-//"` + export SOURCE_BRANCH=`echo $(SERVICE_ARTIFACT_NAME) | cut -d'+' -f1 | sed -e "s/$service_name-//"` fi echo "##vso[task.setvariable variable=BRANCH_NAME]`echo $SOURCE_BRANCH | sed -r 's/[/|\\@":<>?*]+/-/g'`" + env: + service_name: ${{ parameters.service_name }} displayName: Set and replace invalid characters in branch name - - bash: 'echo "##vso[build.updatebuildnumber]${{ parameters.service_name }}-$(BRANCH_NAME)+$(Build.BuildID)"' + - bash: 'echo "##vso[build.updatebuildnumber]$service_name-$(BRANCH_NAME)+$(Build.BuildID)"' condition: eq(variables['Build.SourceBranchName'], 'merge') displayName: Update build ID if this is a pull request + env: + service_name: ${{ parameters.service_name }} diff --git a/azure/utils-pr-pipeline.yml b/azure/utils-pr-pipeline.yml index 7fff0c5f..571eb38c 100644 --- a/azure/utils-pr-pipeline.yml +++ b/azure/utils-pr-pipeline.yml @@ -10,7 +10,7 @@ trigger: pr: branches: - include: ['*'] + include: ["*"] resources: repositories: @@ -20,12 +20,12 @@ resources: ref: refs/heads/master endpoint: NHSDigital -jobs: +jobs: - job: test displayName: Test ansible timeoutInMinutes: 30 pool: - name: 'AWS-ECS' + name: "AWS-ECS" workspace: clean: all steps: @@ -38,14 +38,13 @@ jobs: echo "##vso[task.setvariable variable=PY_VER]$PATCH" echo "Resolved latest python version: $PATCH" echo "##vso[task.setvariable variable=LD_LIBRARY_PATH;]/agent/_work/_tool/Python/${PATCH}/x64/lib/" - displayName: 'Query and set python tool cache path' - + displayName: "Query and set python tool cache path" - task: UsePythonVersion@0 name: UsePy - displayName: 'Use Python 3.13' + displayName: "Use Python 3.13" inputs: - versionSpec: '$(PY_VER)' + versionSpec: "$(PY_VER)" # - bash: | # echo "Checking the python version in use to set LD_LIBRARY_PATH" @@ -82,7 +81,7 @@ jobs: echo "Build.SourceBranchName: $(Build.SourceBranchName)" echo "Build.SourceVersion: $(Build.SourceVersion)" echo "Build.SourceVersionMessage: $(Build.SourceVersionMessage)" - + if [[ ! -z $(NOTIFY_COMMIT_SHA) ]]; then echo "##[debug]Using already provided NOTIFY_COMMIT_SHA=$(NOTIFY_COMMIT_SHA)" else @@ -115,7 +114,7 @@ jobs: parameters: state: pending description: "Testing ansible collection" - + - bash: | ls -R make install @@ -130,7 +129,7 @@ jobs: ANSIBLE_FORCE_COLOR=yes poetry run ansible-test integration --python=3.13 workingDirectory: ansible/collections/ansible_collections/nhsd/apigee displayName: Integration test ansible - + - template: ./components/update-github-status.yml parameters: state: success From ab194ed1a318ef1b04220c7be2d2176710a0eda5 Mon Sep 17 00:00:00 2001 From: Sathiyakumar Ganesan Date: Mon, 11 May 2026 11:53:18 +0100 Subject: [PATCH 16/38] Refactor environment variable handling in YAML files for consistency --- azure/common/apigee-build.yml | 27 +++++++++++++-------------- azure/components/set-facts.yml | 9 ++++++--- 2 files changed, 19 insertions(+), 17 deletions(-) diff --git a/azure/common/apigee-build.yml b/azure/common/apigee-build.yml index b35c1290..5d70ddc1 100644 --- a/azure/common/apigee-build.yml +++ b/azure/common/apigee-build.yml @@ -45,14 +45,13 @@ jobs: variables: ${{ each var in parameters.variables }}: ${{ var.key }}: ${{ var.value }} - env: service_name: ${{ parameters.service_name }} service_short_name: ${{ parameters.short_service_name }} python_version: ${{ parameters.python_version }} steps: - bash: | - if [ -n "$(ls -A "$(Pipeline.Workspace)/s/$service_name" 2>/dev/null)" ]; then + if [ -n "$(ls -A "$(Pipeline.Workspace)/s/$(service_name)" 2>/dev/null)" ]; then echo "workspace directory is not empty!" exit 1 fi @@ -220,15 +219,15 @@ jobs: displayName: "Install utils " - bash: | - export out_dir="$(realpath $service_name/dist)" - export commit_hash="$(git -C $service_name rev-parse --short HEAD)" + export out_dir="$(realpath $(service_name)/dist)" + export commit_hash="$(git -C $(service_name) rev-parse --short HEAD)" # sha prefix is required docker gets upset if names contain -0 iirc export build_label="$(Build.BuildId)-sha${commit_hash}" export pr_number="$(PR_NUMBER)" - export service_name="$service_name" - export service_id="$service_short_name" + export service_name="$(service_name)" + export service_id="$(service_short_name)" export ANSIBLE_FORCE_COLOR=yes - make --no-print-directory -C "$service_name/utils/ansible" create-build-env-vars + make --no-print-directory -C "$(service_name)/utils/ansible" create-build-env-vars displayName: output build env vars for artifact condition: and(succeeded(), eq(variables['build_containers'], 'true')) @@ -238,8 +237,8 @@ jobs: condition: and(succeeded(), eq(variables['build_containers'], 'true')) - bash: | - source "$service_name/dist/.build_env_vars" - account=ptl make --no-print-directory -C "$service_name/utils/ansible" create-ecr-build-role + source "$(service_name)/dist/.build_env_vars" + account=ptl make --no-print-directory -C "$(service_name)/utils/ansible" create-ecr-build-role echo "##vso[task.setvariable variable=BUILD_ROLE]build-${service_id}" displayName: "ensure build role" condition: and(succeeded(), eq(variables['build_containers'], 'true')) @@ -250,9 +249,9 @@ jobs: profile: "$(BUILD_ROLE)" - bash: | - source "$service_name/dist/.build_env_vars" - CONTAINER_VARS_FILE="$(realpath $service_name/ecs-proxies-containers.yml)" \ - make --no-print-directory -C "$service_name/utils/ansible" build-ecs-proxies + source "$(service_name)/dist/.build_env_vars" + CONTAINER_VARS_FILE="$(realpath $(service_name)/ecs-proxies-containers.yml)" \ + make --no-print-directory -C "$(service_name)/utils/ansible" build-ecs-proxies displayName: "Build and push ECS proxies" condition: and(succeeded(), eq(variables['build_containers'], 'true')) @@ -260,7 +259,7 @@ jobs: - ${{ post_ecs_push }} - bash: | - cp "$service_name"/ecs-*.yml "$service_name"/dist || true + cp "$(service_name)"/ecs-*.yml "$(service_name)"/dist || true displayName: "Copy ecs-proxies-deploy configs into build artifact" condition: and(succeeded(), eq(variables['build_containers'], 'true')) @@ -285,7 +284,7 @@ jobs: displayName: "Validate manifest (template) and copy to artifact" - bash: | - cd "$service_name/utils" + cd "$(service_name)/utils" git rev-parse HEAD > ../dist/.utils-version displayName: Snapshot utils version diff --git a/azure/components/set-facts.yml b/azure/components/set-facts.yml index 91dcefad..eef93504 100644 --- a/azure/components/set-facts.yml +++ b/azure/components/set-facts.yml @@ -16,9 +16,6 @@ parameters: - name: apigee_organization type: string -env: - service_name: ${{ parameters.service_name }} - steps: - bash: | set -euo pipefail @@ -27,11 +24,15 @@ steps: export SERVICE_ARTIFACT_NAME=`ls -t $(Pipeline.Workspace)/s/$service_name` echo "##vso[task.setvariable variable=SERVICE_ARTIFACT_NAME]$SERVICE_ARTIFACT_NAME" echo "Set Artifact Name of: $SERVICE_ARTIFACT_NAME" + env: + service_name: ${{ parameters.service_name }} displayName: "Set SERVICE_ARTIFACT_NAME" - bash: | echo "##vso[task.setvariable variable=SERVICE_DIR]$(Pipeline.Workspace)/s/$service_name/$(SERVICE_ARTIFACT_NAME)" echo "##vso[task.setvariable variable=UTILS_DIR]$(Pipeline.Workspace)/s/$service_name/$(SERVICE_ARTIFACT_NAME)/utils" + env: + service_name: ${{ parameters.service_name }} displayName: "Set SERVICE_DIR and UTILS_DIR for deploy-service" - bash: | @@ -48,6 +49,8 @@ steps: - bash: 'echo "##vso[build.updatebuildnumber]$service_name-$(BRANCH_NAME)+$(Build.BuildID)"' condition: eq(variables['Build.SourceBranchName'], 'merge') + env: + service_name: ${{ parameters.service_name }} displayName: Update build ID if this is a pull request - template: ../components/get-aws-secrets-and-ssm-params.yml From 039bbd47ba4cbb9bca6486e812ed312ed60fa5fa Mon Sep 17 00:00:00 2001 From: Sathiyakumar Ganesan Date: Mon, 11 May 2026 11:57:23 +0100 Subject: [PATCH 17/38] Fix variable assignment syntax for Python version in apigee-build.yml --- azure/common/apigee-build.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/azure/common/apigee-build.yml b/azure/common/apigee-build.yml index 5d70ddc1..befd8a7e 100644 --- a/azure/common/apigee-build.yml +++ b/azure/common/apigee-build.yml @@ -147,7 +147,7 @@ jobs: service_name: "${{ parameters.service_name }}" - bash: | - pyversion="$python_version" + pyversion="$(python_version)" PATCH=$(curl -fsSL https://raw.githubusercontent.com/actions/python-versions/main/versions-manifest.json \ | jq -r --arg pyversion "$pyversion" '[ .[] | select(.stable == true) | .version | select(test("^" + ($pyversion|gsub("\\.";"\\.")) + "\\.\\d+$")) | split(".") | map(tonumber)] | max | join(".")') From 632dc7760abd92f82b2b6892388fee5c6beba550 Mon Sep 17 00:00:00 2001 From: Sathiyakumar Ganesan Date: Mon, 11 May 2026 12:13:24 +0100 Subject: [PATCH 18/38] Refactor variable usage in deploy-service.yml for consistency and clarity --- azure/templates/deploy-service.yml | 60 +++++++++++++++--------------- 1 file changed, 30 insertions(+), 30 deletions(-) diff --git a/azure/templates/deploy-service.yml b/azure/templates/deploy-service.yml index f8f9bf2a..bbf024d5 100644 --- a/azure/templates/deploy-service.yml +++ b/azure/templates/deploy-service.yml @@ -49,7 +49,7 @@ parameters: - portal_api_requires_callback_url - spec_file -env: +variables: service_name: ${{ parameters.service_name }} apigee_environment: ${{ parameters.apigee_environment }} aws_account: ${{ parameters.aws_account }} @@ -70,7 +70,7 @@ steps: displayName: Warn ${{ variable_name }} is a deprecated pipeline parameter - bash: | ls -R - if [[ -f ecs-deploy-${apigee_environment}.yml ]] || [[ -f ecs-proxies-deploy.yml ]] || [[ -f ecs-proxies-deploy-sandbox.yml ]]; then + if [[ -f ecs-deploy-$(apigee_environment).yml ]] || [[ -f ecs-proxies-deploy.yml ]] || [[ -f ecs-proxies-deploy-sandbox.yml ]]; then echo "##vso[task.setvariable variable=deploy_containers]true" else echo "##vso[task.setvariable variable=deploy_containers]false" @@ -104,7 +104,7 @@ steps: - ${{ if parameters.proxy_path }}: - bash: | - echo "Deploying $service_name artifact $(SERVICE_ARTIFACT_NAME) as $fully_qualified_service_name to $service_base_path on $(ENV_URL)" + echo "Deploying $(service_name) artifact $(SERVICE_ARTIFACT_NAME) as $(fully_qualified_service_name) to $(service_base_path) on $(ENV_URL)" displayName: "Deploy Info" - ${{ each pre_template_step in parameters.pre_template }}: @@ -131,10 +131,10 @@ steps: - bash: | set -euo pipefail - export SERVICE_NAME="$service_name" + export SERVICE_NAME="$(service_name)" export PROXIES_DIR="$(SERVICE_DIR)/proxies" - export SERVICE_BASE_PATH="$service_base_path" - export APIGEE_ENVIRONMENT="$apigee_environment" + export SERVICE_BASE_PATH="$(service_base_path)" + export APIGEE_ENVIRONMENT="$(apigee_environment)" export HOSTED_TARGET_CONNECTION_PATH_SUFFIX="$hosted_target_connection_path_suffix" export HOSTED_TARGET_HEALTHCHECK_SUFFIX="$hosted_target_healthcheck_suffix" export SOURCE_COMMIT_ID=$(Build.SourceVersion) @@ -144,7 +144,7 @@ steps: if [[ ! -z $ASSUMED_VERSION ]]; then export DEPLOYED_VERSION=$ASSUMED_VERSION else - export DEPLOYED_VERSION="$fully_qualified_service_name" + export DEPLOYED_VERSION="$(fully_qualified_service_name)" fi if [ -f $(SERVICE_DIR)/.build_env_vars ]; then @@ -157,7 +157,7 @@ steps: if [ -f $(SERVICE_DIR)/manifest.yml ]; then DIST_DIR=$(SERVICE_DIR) \ - PROXY_DIR=$proxy_path \ + PROXY_DIR=$(proxy_path) \ ANSIBLE_FORCE_COLOR=yes \ make --no-print-directory -C ansible add-apim-guids-policy fi @@ -177,8 +177,8 @@ steps: proxy_vars_file="" - if [ -f $(SERVICE_DIR)/ecs-deploy-${apigee_environment}.yml ]; then - proxy_vars_file="$(SERVICE_DIR)/ecs-deploy-${apigee_environment}.yml" + if [ -f $(SERVICE_DIR)/ecs-deploy-$(apigee_environment).yml ]; then + proxy_vars_file="$(SERVICE_DIR)/ecs-deploy-$(apigee_environment).yml" else if [ -f $(SERVICE_DIR)/ecs-deploy-all.yml ]; then proxy_vars_file="$(SERVICE_DIR)/ecs-deploy-all.yml" @@ -188,11 +188,11 @@ steps: if [[ ! -z "${proxy_vars_file}" ]]; then source $(SERVICE_DIR)/.build_env_vars - deploy_role="deploy-${apigee_environment}-${service_id}" + deploy_role="deploy-$(apigee_environment)-${service_id}" - account=${aws_account} \ - SERVICE_BASE_PATH=${service_base_path} \ - APIGEE_ENVIRONMENT=${apigee_environment} \ + account=$(aws_account) \ + SERVICE_BASE_PATH=$(service_base_path) \ + APIGEE_ENVIRONMENT=$(apigee_environment) \ make --no-print-directory -C $(UTILS_DIR)/ansible create-api-deployment-pre-reqs fi @@ -203,7 +203,7 @@ steps: condition: and(succeeded(), eq(variables['deploy_containers'], 'true')) - bash: | - echo "##vso[task.setvariable variable=SERVICE_BASE_PATH_FOR_CONDITION]$service_base_path" + echo "##vso[task.setvariable variable=SERVICE_BASE_PATH_FOR_CONDITION]$(service_base_path)" displayName: Set SERVICE_BASE_PATH_FOR_CONDITION - bash: | @@ -212,13 +212,13 @@ steps: export CONTAINER_VARS_FILE="$SERVICE_DIR/ecs-proxies-containers.yml" source $(SERVICE_DIR)/.build_env_vars - export SERVICE_BASE_PATH="$service_base_path" + export SERVICE_BASE_PATH="$(service_base_path)" echo "SERVICE_BASE_PATH='${SERVICE_BASE_PATH}'" export ASSUMED_VERSION=$(echo $SERVICE_ARTIFACT_NAME | grep -E -o "v[0-9]+\.[0-9]+\.[0-9]+-?[a-z]*" || true | tail -1) - export DEPLOYED_VERSION=${ASSUMED_VERSION:-$fully_qualified_service_name} + export DEPLOYED_VERSION=${ASSUMED_VERSION:-$(fully_qualified_service_name)} - account=$aws_account \ + account=$(aws_account) \ CONTAINER_VARS_FILE="${CONTAINER_VARS_FILE}" \ SOURCE_COMMIT_ID="$(Build.SourceVersion)" \ RELEASE_RELEASEID="$(Build.BuildId)" \ @@ -243,7 +243,7 @@ steps: if [[ ! -z $ASSUMED_VERSION ]]; then export DEPLOYED_VERSION=$ASSUMED_VERSION else - export DEPLOYED_VERSION="$fully_qualified_service_name" + export DEPLOYED_VERSION="$(fully_qualified_service_name)" fi @@ -258,12 +258,12 @@ steps: export use_ecs_tag=$TF_VAR_use_ecs_tag - account=$aws_account \ + account=$(aws_account) \ PROXY_VARS_FILE="${proxy_vars_file}" \ SOURCE_COMMIT_ID="$(Build.SourceVersion)" \ RELEASE_RELEASEID="$(Build.BuildId)" \ - SERVICE_BASE_PATH=$service_base_path \ - APIGEE_ENVIRONMENT=$apigee_environment \ + SERVICE_BASE_PATH=$(service_base_path) \ + APIGEE_ENVIRONMENT=$(apigee_environment) \ use_ecs_tag="${use_ecs_tag}" \ make --no-print-directory -C $(UTILS_DIR)/ansible deploy-ecs-proxies @@ -274,13 +274,13 @@ steps: - bash: | set -euo pipefail - export PROXY_DIR="$(SERVICE_DIR)/proxies/$proxy_path" - export SERVICE_NAME="$service_name" - export FULLY_QUALIFIED_SERVICE_NAME="$fully_qualified_service_name" - export SERVICE_BASE_PATH="$service_base_path" + export PROXY_DIR="$(SERVICE_DIR)/proxies/$(proxy_path)" + export SERVICE_NAME="$(service_name)" + export FULLY_QUALIFIED_SERVICE_NAME="$(fully_qualified_service_name)" + export SERVICE_BASE_PATH="$(service_base_path)" export APIGEE_ACCESS_TOKEN="$(secret.AccessToken)" - export APIGEE_ENVIRONMENT="$apigee_environment" - export APIGEE_ORGANIZATION="nhsd-$apigee_organization" + export APIGEE_ENVIRONMENT="$(apigee_environment)" + export APIGEE_ORGANIZATION="nhsd-$(apigee_organization)" export PING="$ping" export ANSIBLE_FORCE_COLOR=yes @@ -293,8 +293,8 @@ steps: set -euo pipefail export APIGEE_ACCESS_TOKEN="$(secret.AccessToken)" - export APIGEE_ENVIRONMENT="$apigee_environment" - export APIGEE_ORGANIZATION="nhsd-$apigee_organization" + export APIGEE_ENVIRONMENT="$(apigee_environment)" + export APIGEE_ORGANIZATION="nhsd-$(apigee_organization)" export ANSIBLE_FORCE_COLOR=yes export DIST_DIR=$(SERVICE_DIR) export PULL_REQUEST="$pr_label" From dac4c6dda1abf4557b74cd27c94534fec5439b61 Mon Sep 17 00:00:00 2001 From: Sathiyakumar Ganesan Date: Mon, 11 May 2026 12:34:58 +0100 Subject: [PATCH 19/38] Refactor YAML scripts for improved consistency and clarity in variable usage --- azure/cleanup-ecs-pr-proxies.yml | 29 +++++++----------- azure/cleanup-pr-portal-apis-and-specs.yml | 5 ++-- azure/utils-pr-pipeline.yml | 34 +++++++++++----------- 3 files changed, 29 insertions(+), 39 deletions(-) diff --git a/azure/cleanup-ecs-pr-proxies.yml b/azure/cleanup-ecs-pr-proxies.yml index c6479bcc..5e2b7f00 100644 --- a/azure/cleanup-ecs-pr-proxies.yml +++ b/azure/cleanup-ecs-pr-proxies.yml @@ -30,13 +30,12 @@ jobs: displayName: Cleanup ECS PR Proxies timeoutInMinutes: 360 pool: - name: 'AWS-ECS' + name: "AWS-ECS" workspace: clean: all steps: - - checkout: self - bash: | @@ -52,13 +51,13 @@ jobs: - bash: | tfenv use 1.2.3 displayName: setup terraform - + - task: s3-cache-action@1 inputs: key: poetry | utils | poetry.lock location: ".venv" debug: true - displayName: cache utils pre-requisites + displayName: cache utils pre-requisites - bash: | pyversion="3.13" @@ -69,34 +68,26 @@ jobs: echo "##vso[task.setvariable variable=PY_VER]$PATCH" echo "Resolved latest python version: $PATCH" echo "##vso[task.setvariable variable=LD_LIBRARY_PATH;]/agent/_work/_tool/Python/${PATCH}/x64/lib/" - displayName: 'Query and set python tool cache path' - + displayName: "Query and set python tool cache path" - task: UsePythonVersion@0 name: UsePy - displayName: 'Use Python 3.13' + displayName: "Use Python 3.13" inputs: - versionSpec: '$(PY_VER)' - - # - bash: | - # echo "Checking the python version in use to set LD_LIBRARY_PATH" - # echo "Python location: $(UsePy.pythonLocation)" - # echo "##vso[task.setvariable variable=LD_LIBRARY_PATH]$(UsePy.pythonLocation)/lib" - # displayName: 'Set LD_LIBRARY_PATH' + versionSpec: "$(PY_VER)" - bash: | make install displayName: install dependencies - ${{ each retry in parameters.retries }}: - - template: ./components/cleanup-ecs-pr-proxies-job.yml - parameters: - retry: '${{ retry }}' - retain_hours: '${{ parameters.retain_hours }}' + - template: ./components/cleanup-ecs-pr-proxies-job.yml + parameters: + retry: "${{ retry }}" + retain_hours: "${{ parameters.retain_hours }}" - bash: | echo "AWS role session has timed out after multiple retries" exit -1 displayName: Trigger failure if role has timed out condition: eq(variables['should_retry'], 'true') - diff --git a/azure/cleanup-pr-portal-apis-and-specs.yml b/azure/cleanup-pr-portal-apis-and-specs.yml index b4ffc940..d636fbdc 100644 --- a/azure/cleanup-pr-portal-apis-and-specs.yml +++ b/azure/cleanup-pr-portal-apis-and-specs.yml @@ -16,12 +16,11 @@ jobs: displayName: Cleanup PR specs timeoutInMinutes: 240 pool: - name: 'AWS-ECS' + name: "AWS-ECS" workspace: clean: all steps: - - checkout: self - bash: | @@ -48,7 +47,7 @@ jobs: key: poetry | utils | poetry.lock location: ".venv" debug: true - alias: 'Utils' + alias: "Utils" displayName: cache utils pre-requisites - bash: | diff --git a/azure/utils-pr-pipeline.yml b/azure/utils-pr-pipeline.yml index 571eb38c..bfdf4915 100644 --- a/azure/utils-pr-pipeline.yml +++ b/azure/utils-pr-pipeline.yml @@ -46,12 +46,6 @@ jobs: inputs: versionSpec: "$(PY_VER)" - # - bash: | - # echo "Checking the python version in use to set LD_LIBRARY_PATH" - # echo "Python location: $(UsePy.pythonLocation)" - # echo "##vso[task.setvariable variable=LD_LIBRARY_PATH]$(UsePy.pythonLocation)/lib" - # displayName: 'Set LD_LIBRARY_PATH' - - bash: | instance_id="$(curl -s http://169.254.169.254/latest/meta-data/instance-id)" echo instance-id: "${instance_id}" @@ -77,36 +71,42 @@ jobs: - /ptl/azure-devops/GITHUB_USER - bash: | - echo "Build.SourceBranch: $(Build.SourceBranch)" - echo "Build.SourceBranchName: $(Build.SourceBranchName)" - echo "Build.SourceVersion: $(Build.SourceVersion)" - echo "Build.SourceVersionMessage: $(Build.SourceVersionMessage)" + echo "Build.SourceBranch: $Build_SourceBranch" + echo "Build.SourceBranchName: $Build_SourceBranchName" + echo "Build.SourceVersion: $Build_SourceVersion" + echo "Build.SourceVersionMessage: $Build_SourceVersionMessage" - if [[ ! -z $(NOTIFY_COMMIT_SHA) ]]; then - echo "##[debug]Using already provided NOTIFY_COMMIT_SHA=$(NOTIFY_COMMIT_SHA)" + if [[ ! -z $NOTIFY_COMMIT_SHA ]]; then + echo "##[debug]Using already provided NOTIFY_COMMIT_SHA=$NOTIFY_COMMIT_SHA" else NOTIFY_COMMIT_SHA="" - if [[ "$(Build.SourceBranch)" =~ ^refs/tags/.+$ ]]; then + if [[ "$Build_SourceBranch" =~ ^refs/tags/.+$ ]]; then echo "##[debug]Build appears to be a tag build" echo "##[debug]Using Build.SourceVersion as NOTIFY_COMMIT_SHA" - NOTIFY_COMMIT_SHA="$(Build.SourceVersion)" + NOTIFY_COMMIT_SHA="$Build_SourceVersion" fi - if [[ "$(Build.SourceBranch)" =~ ^refs/pull/.+$ ]]; then + if [[ "$Build_SourceBranch" =~ ^refs/pull/.+$ ]]; then echo "##[debug]Build appears to be a pull request build" echo "##[debug]Extracting NOTIFY_COMMIT_SHA from Build.SourceVersionMessage" - NOTIFY_COMMIT_SHA=`echo "$(Build.SourceVersionMessage)" | cut -d' ' -f2` + NOTIFY_COMMIT_SHA=`echo "$Build_SourceVersionMessage" | cut -d' ' -f2` fi if [[ -z $NOTIFY_COMMIT_SHA ]]; then echo "##[debug]Build does not appear to be pull or tag build" echo "##[debug]Using Build.SourceVersion as NOTIFY_COMMIT_SHA" - NOTIFY_COMMIT_SHA="$(Build.SourceVersion)" + NOTIFY_COMMIT_SHA="$Build.SourceVersion" fi echo "##vso[task.setvariable variable=NOTIFY_COMMIT_SHA]$NOTIFY_COMMIT_SHA" fi + env: + Build_SourceBranch: $(Build.SourceBranch) + Build_SourceBranchName: $(Build.SourceBranchName) + Build_SourceVersion: $(Build.SourceVersion) + Build_SourceVersionMessage: $(Build.SourceVersionMessage) + NOTIFY_COMMIT_SHA: $(NOTIFY_COMMIT_SHA) displayName: Set NOTIFY_COMMIT_SHA condition: always() From 90af4e98311ae35579e42a69f802a0e74e0d432a Mon Sep 17 00:00:00 2001 From: Sathiyakumar Ganesan Date: Mon, 11 May 2026 12:57:01 +0100 Subject: [PATCH 20/38] Enhance manifest validation to conditionally update spec paths based on dist_dir presence --- .../plugins/module_utils/models/ansible/validate_manifest.py | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/ansible/collections/ansible_collections/nhsd/apigee/plugins/module_utils/models/ansible/validate_manifest.py b/ansible/collections/ansible_collections/nhsd/apigee/plugins/module_utils/models/ansible/validate_manifest.py index a8777fca..f5c0bed4 100644 --- a/ansible/collections/ansible_collections/nhsd/apigee/plugins/module_utils/models/ansible/validate_manifest.py +++ b/ansible/collections/ansible_collections/nhsd/apigee/plugins/module_utils/models/ansible/validate_manifest.py @@ -55,13 +55,16 @@ def prepend_dist_dir_to_spec_paths(cls, manifest, values): print(dist_dir) if not dist_dir: return manifest + changed = False apigee = manifest["apigee"] for env_dict in apigee["environments"]: for spec_dict in env_dict["specs"]: path = spec_dict.get("path") if path is not None: spec_dict["path"] = os.path.join(dist_dir, path) - return manifest + changed = True + + return manifest if changed else manifest @pydantic.validator("manifest") def check_namespacing(cls, manifest, values): From 4e622d786b0b25a7c938bee97e0756194da7415f Mon Sep 17 00:00:00 2001 From: Sathiyakumar Ganesan Date: Mon, 11 May 2026 14:13:59 +0100 Subject: [PATCH 21/38] Refactor function signatures in env_cleaner.py to remove unused parameters and improve docstring formatting --- env_cleaner.py | 9 ++++----- 1 file changed, 4 insertions(+), 5 deletions(-) diff --git a/env_cleaner.py b/env_cleaner.py index cc912e69..b9243350 100644 --- a/env_cleaner.py +++ b/env_cleaner.py @@ -19,13 +19,13 @@ --min-age= (NOT IMPLEMENTED) Minimum age in seconds --respect-prs Don't undeploy if there's a PR open for it """ + import re import requests from typing import Optional, Set from docopt import docopt from apigee_client import ApigeeClient - SPEC_MATCHER = re.compile("(personal-demographics|identity-service|hello-world)-.+") PROXY_MATCHER = re.compile( "(personal-demographics|identity-service|hello-world)-internal-dev-.+" @@ -49,7 +49,7 @@ def canonicalize(name: str) -> str: class GithubClient: def get_open_prs(self, env: str) -> Set[str]: - """ Returns names of open pull requests """ + """Returns names of open pull requests""" canonical_prs = set() for repo_name in REPO_NAMES.keys(): prs = requests.get( @@ -65,7 +65,7 @@ def get_open_prs(self, env: str) -> Set[str]: return canonical_prs -def clean_specs(client: ApigeeClient, env: str, dry_run: bool = False): +def clean_specs(client: ApigeeClient, dry_run: bool = False): specs = client.list_specs() spec_names = [spec["name"] for spec in specs["contents"]] @@ -85,7 +85,6 @@ def clean_proxies( env: str, dry_run: bool = False, sandboxes_only: bool = False, - min_age: Optional[int] = None, undeploy_only: bool = False, respect_prs: bool = False, ): @@ -135,7 +134,7 @@ def clean_proxies( client.delete_proxy(proxy) -def clean_products(client: ApigeeClient, env: str, dry_run: bool = False): +def clean_products(client: ApigeeClient, dry_run: bool = False): products = client.list_products() pr_products = [product for product in products if PRODUCT_MATCHER.match(product)] From 70751893eab4b70549c435a67496774f24e38cfb Mon Sep 17 00:00:00 2001 From: Sathiyakumar Ganesan Date: Mon, 11 May 2026 14:14:31 +0100 Subject: [PATCH 22/38] Fix formatting in add_policy_to_pre_flow.py and update type hint in apigee_action.py --- .../nhsd/apigee/plugins/action/add_policy_to_pre_flow.py | 7 +++---- .../nhsd/apigee/plugins/module_utils/apigee_action.py | 2 +- 2 files changed, 4 insertions(+), 5 deletions(-) diff --git a/ansible/collections/ansible_collections/nhsd/apigee/plugins/action/add_policy_to_pre_flow.py b/ansible/collections/ansible_collections/nhsd/apigee/plugins/action/add_policy_to_pre_flow.py index 46b82086..92dbedbe 100644 --- a/ansible/collections/ansible_collections/nhsd/apigee/plugins/action/add_policy_to_pre_flow.py +++ b/ansible/collections/ansible_collections/nhsd/apigee/plugins/action/add_policy_to_pre_flow.py @@ -57,13 +57,12 @@ def run(self, tmp=None, task_vars=None): name = step.find("Name") if name.text == args.policy_name: return {"changed": False} - break result = {"changed": True} if diff_mode: result["diff"] = { "before": etree.tostring(tree, pretty_print=True).decode(), - "before_header": str(proxies_file) + "before_header": str(proxies_file), } step = etree.Element("Step") @@ -76,8 +75,8 @@ def run(self, tmp=None, task_vars=None): result["diff"].update( { "after": etree.tostring(tree, pretty_print=True).decode(), - "after_header": str(proxies_file) - } + "after_header": str(proxies_file), + } ) if check_mode or result["changed"] is False: diff --git a/ansible/collections/ansible_collections/nhsd/apigee/plugins/module_utils/apigee_action.py b/ansible/collections/ansible_collections/nhsd/apigee/plugins/module_utils/apigee_action.py index 428b4372..85f05b67 100644 --- a/ansible/collections/ansible_collections/nhsd/apigee/plugins/module_utils/apigee_action.py +++ b/ansible/collections/ansible_collections/nhsd/apigee/plugins/module_utils/apigee_action.py @@ -4,7 +4,7 @@ class ApigeeAction(ansible.plugins.action.ActionBase): - def validate_args(self, Validator: pydantic.BaseModel): + def validate_args(self, Validator: type[pydantic.BaseModel]): """Returns two-length tuple of validated_args and errors dicts.""" try: args = Validator(**self._task.args) From c25f7bab5a1e8dbf4f81edccd94f5d9a9527e35a Mon Sep 17 00:00:00 2001 From: Sathiyakumar Ganesan Date: Mon, 11 May 2026 14:15:01 +0100 Subject: [PATCH 23/38] Refactor type hints in product.py and rate_limiting_config.py to use Optional for nullable fields --- .../module_utils/models/apigee/product.py | 17 +++++++++++------ .../models/apigee/rate_limiting_config.py | 17 +++++++++-------- 2 files changed, 20 insertions(+), 14 deletions(-) diff --git a/ansible/collections/ansible_collections/nhsd/apigee/plugins/module_utils/models/apigee/product.py b/ansible/collections/ansible_collections/nhsd/apigee/plugins/module_utils/models/apigee/product.py index da473499..905c6dc7 100644 --- a/ansible/collections/ansible_collections/nhsd/apigee/plugins/module_utils/models/apigee/product.py +++ b/ansible/collections/ansible_collections/nhsd/apigee/plugins/module_utils/models/apigee/product.py @@ -1,5 +1,5 @@ import json -from typing import Union, Literal, List, Dict, Any, Type +from typing import Optional, Union, Literal, List, Dict, Any, Type from typing_extensions import Annotated from pydantic import ( Field, @@ -110,19 +110,22 @@ def _literal_name(class_): + ")$)" ) + class ApigeeProductAttributeOther(BaseModel): name: constr(regex=PRODUCT_ATTRIBUTE_REGEX) value: str + ApigeeProductAttributeSpecial = Annotated[ - Union [ + Union[ ApigeeProductAttributeAccess, ApigeeProductAttributeRateLimit, ApigeeProductAttributeRateLimiting, ], - Field(discriminator="name") + Field(discriminator="name"), ] + def _count_cls(items: List[Any], cls: Type): return sum(isinstance(item, cls) for item in items) @@ -130,9 +133,11 @@ def _count_cls(items: List[Any], cls: Type): class ApigeeProduct(BaseModel): name: str approvalType: Literal["auto", "manual"] = "manual" - attributes: List[Union[ApigeeProductAttributeSpecial, ApigeeProductAttributeOther]] = [{"name": "access", "value": "private"}] - description: str = None - displayName: str = None + attributes: List[ + Union[ApigeeProductAttributeSpecial, ApigeeProductAttributeOther] + ] = [{"name": "access", "value": "private"}] + description: Optional[str] = None + displayName: Optional[str] = None # Note: This value is manually inserted by apigee_environment # object that contains this product. So if you do not provide a diff --git a/ansible/collections/ansible_collections/nhsd/apigee/plugins/module_utils/models/apigee/rate_limiting_config.py b/ansible/collections/ansible_collections/nhsd/apigee/plugins/module_utils/models/apigee/rate_limiting_config.py index 404af292..967c412d 100644 --- a/ansible/collections/ansible_collections/nhsd/apigee/plugins/module_utils/models/apigee/rate_limiting_config.py +++ b/ansible/collections/ansible_collections/nhsd/apigee/plugins/module_utils/models/apigee/rate_limiting_config.py @@ -2,13 +2,13 @@ Pydantic class for the rateliming config JSON, attached to products and apps to control the ApplyRateLimiting shared flow. """ -from typing import Literal + +from typing import Literal, Optional from pydantic import BaseModel, conint, constr, Extra class ExcludeNoneModel(BaseModel): - """ Providing default values for ratelimiting here would mean that changing defaults required a redeploy for all proxies. @@ -21,26 +21,27 @@ class ExcludeNoneModel(BaseModel): update the defaults for everyone by just by updating the shared flow. """ + def dict(self, **kwargs): kwargs["exclude_none"] = True return super().dict(**kwargs) class Config: - extra=Extra.forbid + extra = Extra.forbid class QuotaConfig(ExcludeNoneModel): - enabled: bool = None + enabled: Optional[bool] = None interval: conint(gt=0) = None limit: conint(gt=0) = None timeunit: Literal["minute", "hour", "day", "week", "month"] = None class SpikeArrestConfig(ExcludeNoneModel): - enabled: bool = None - ratelimit: constr(regex=r"^[1-9][0-9]*(ps|pm)$") = None + enabled: Optional[bool] = None + ratelimit: Optional[constr(regex=r"^[1-9][0-9]*(ps|pm)$")] = None class RateLimitingConfig(ExcludeNoneModel): - quota: QuotaConfig = None - spikeArrest: SpikeArrestConfig = None + quota: Optional[QuotaConfig] = None + spikeArrest: Optional[SpikeArrestConfig] = None From a4eb8239de3b84e492c17f0bc9cdaa54c9eae0b9 Mon Sep 17 00:00:00 2001 From: Sathiyakumar Ganesan Date: Mon, 11 May 2026 14:16:05 +0100 Subject: [PATCH 24/38] Refactor code for improved readability and consistency in update_schema.py, ecs_service.py, and filter_api_products.py --- .../nhsd/apigee/scripts/update_schema.py | 45 +++++++----- ansible/filter_plugins/ecs_service.py | 73 +++++++++++-------- ansible/library/filter_api_products.py | 20 +++-- 3 files changed, 81 insertions(+), 57 deletions(-) diff --git a/ansible/collections/ansible_collections/nhsd/apigee/scripts/update_schema.py b/ansible/collections/ansible_collections/nhsd/apigee/scripts/update_schema.py index e2881726..2d5e56f5 100644 --- a/ansible/collections/ansible_collections/nhsd/apigee/scripts/update_schema.py +++ b/ansible/collections/ansible_collections/nhsd/apigee/scripts/update_schema.py @@ -8,8 +8,9 @@ import typing from ansible_collections.nhsd.apigee.plugins.module_utils.models.manifest import meta -from ansible_collections.nhsd.apigee.plugins.module_utils.models.manifest.manifest import Manifest - +from ansible_collections.nhsd.apigee.plugins.module_utils.models.manifest.manifest import ( + Manifest, +) SCHEMA_VERSION_REGEX = re.compile(r"^([1-9][0-9]*)\.([0-9]+)\.([0-9]+)$") @@ -23,7 +24,9 @@ def __init__(self, schema_or_major: typing.Union[str, int], minor=None, patch=No match = re.match(SCHEMA_VERSION_REGEX, schema_version) if not match: raise ValueError(f"Invalid SchemaString {schema_version}") - self.major, self.minor, self.patch = [int(match.group(x)) for x in range(1, 4)] + self.major, self.minor, self.patch = [ + int(match.group(x)) for x in range(1, 4) + ] elif isinstance(schema_or_major, int): if not isinstance(minor, int) and isinstance(patch, int): raise ValueError("SchemaString.__init__ requires major, minor, patch") @@ -47,7 +50,11 @@ def __lt__(self, other): return ( self.major < other.major or (self.major == other.major and self.minor < other.minor) - or (self.major == other.major and self.minor == other.minor and self.patch < other.patch) + or ( + self.major == other.major + and self.minor == other.minor + and self.patch < other.patch + ) ) def __le__(self, other): @@ -63,20 +70,18 @@ def valid_increments(self): return [ SchemaString(self.major + 1, 0, 0), SchemaString(self.major, self.minor + 1, 0), - SchemaString(self.major, self.minor, self.patch + 1) + SchemaString(self.major, self.minor, self.patch + 1), ] def main(): - """ - - """ + """ """ UPDATED_SCHEMA_VERSION = SchemaString(meta.SCHEMA_VERSION) script_dir = pathlib.Path(__file__).parent - relative_schema_dir = script_dir.joinpath(f"../tests/unit/plugins/module_utils/models/manifest/schema_versions/") - - # last_schema_file_name = pathlib.Path(f"v{}.json") + relative_schema_dir = script_dir.joinpath( + "../tests/unit/plugins/module_utils/models/manifest/schema_versions/" + ) schema_dir_glob = relative_schema_dir.glob("v*.json") @@ -103,14 +108,19 @@ def main(): deltas = [delta for delta in deltas] if not deltas: - raise ValueError(f"No difference between proposed {UPDATED_SCHEMA_VERSION} schema and current {meta.SCHEMA_VERSION}") + raise ValueError( + f"No difference between proposed {UPDATED_SCHEMA_VERSION} schema and current {meta.SCHEMA_VERSION}" + ) if UPDATED_SCHEMA_VERSION not in SCHEMA_VERSION.valid_increments(): - raise ValueError(f"""{UPDATED_SCHEMA_VERSION} is invalid increment after current {SCHEMA_VERSION}. + raise ValueError( + f"""{UPDATED_SCHEMA_VERSION} is invalid increment after current {SCHEMA_VERSION}. Please increment major, minor or patch integer, e.g: -""" + f"\n".join(str(x) for x in SCHEMA_VERSION.valid_increments())) +""" + + "\n".join(str(x) for x in SCHEMA_VERSION.valid_increments()) + ) - print("-"*50) + print("-" * 50) for delta in deltas: if delta.startswith("+"): col = colorama.Fore.GREEN @@ -122,7 +132,7 @@ def main(): print(colorama.Fore.RESET) _input = None - print("-"*50) + print("-" * 50) print("Confirm spec changes? ", end="") while _input not in ["y", "n"]: if _input is not None: @@ -139,8 +149,7 @@ def main(): with open(new_schema_file, "w") as f: f.write(new_schema) - print( - f"""Wrote {new_schema_file}" + print(f"""Wrote {new_schema_file}" Validate this file by executing $ make test in {script_dir.parent}""") diff --git a/ansible/filter_plugins/ecs_service.py b/ansible/filter_plugins/ecs_service.py index 13fb3e5f..695326ff 100644 --- a/ansible/filter_plugins/ecs_service.py +++ b/ansible/filter_plugins/ecs_service.py @@ -11,55 +11,61 @@ secrets=[], expose=True, essential=True, - health_check=dict( - matcher="200", - path="/" - ), - lb_protocol="HTTP" - + health_check=dict(matcher="200", path="/"), + lb_protocol="HTTP", ) def as_ecs_service(docker_service): - ecs_service = [combine(_container_defaults, container) for container in docker_service] + ecs_service = [ + combine(_container_defaults, container) for container in docker_service + ] if sum(1 for _ in ecs_service if _["expose"]) != 1: - raise AnsibleFilterError("you must have exactly one exposed service, set 'expose: false' on others") + raise AnsibleFilterError( + "you must have exactly one exposed service, set 'expose: false' on others" + ) - port_counts = Counter(container['port'] for container in ecs_service) + port_counts = Counter(container["port"] for container in ecs_service) if port_counts.get(9000, 0) != 1: - raise AnsibleFilterError("ecs_service should expose exactly one container with hostPort 9000") + raise AnsibleFilterError( + "ecs_service should expose exactly one container with hostPort 9000" + ) for port, count in port_counts.items(): if count > 1: - raise AnsibleFilterError(f"ecs_service should not have multiple containers on the same port ({port})") + raise AnsibleFilterError( + f"ecs_service should not have multiple containers on the same port ({port})" + ) - if sum(1 for _ in ecs_service if _.get('name') is None): + if sum(1 for _ in ecs_service if _.get("name") is None): raise AnsibleFilterError("all ecs containers should hava name") - name_counts = Counter(container['name'] for container in ecs_service) + name_counts = Counter(container["name"] for container in ecs_service) for name, count in name_counts.items(): if count > 1: - raise AnsibleFilterError(f"ecs_service should not have multiple containers with the same name ({name})") + raise AnsibleFilterError( + f"ecs_service should not have multiple containers with the same name ({name})" + ) return ecs_service _app_scaling_defaults = dict( - service_metric='', + service_metric="", target_value=0, scale_in_cooldown=300, scale_out_cooldown=60, - enabled=False + enabled=False, ) _ecs_service_metrics = ( - 'ALBRequestCountPerTarget', - 'ECSServiceAverageCPUUtilization', - 'ECSServiceAverageMemoryUtilization' + "ALBRequestCountPerTarget", + "ECSServiceAverageCPUUtilization", + "ECSServiceAverageMemoryUtilization", ) @@ -69,25 +75,31 @@ def as_ecs_autoscaling(docker_service_autoscaling): return _app_scaling_defaults autoscaling_policy = combine(_app_scaling_defaults, docker_service_autoscaling) - service_metric = autoscaling_policy.get('service_metric') + service_metric = autoscaling_policy.get("service_metric") if service_metric not in _ecs_service_metrics: - raise AnsibleFilterError(f"scaling policy must have a service_metric in set {_ecs_service_metrics}") + raise AnsibleFilterError( + f"scaling policy must have a service_metric in set {_ecs_service_metrics}" + ) - target_value = str(autoscaling_policy.get('target_value', '')) + target_value = str(autoscaling_policy.get("target_value", "")) if not target_value.isdigit(): - raise AnsibleFilterError(f"scaling policy must have a target_value as a positive integer") + raise AnsibleFilterError( + "scaling policy must have a target_value as a positive integer" + ) target_value = int(target_value) - autoscaling_policy['target_value'] = target_value - autoscaling_policy['enabled'] = True + autoscaling_policy["target_value"] = target_value + autoscaling_policy["enabled"] = True if target_value < 10: - raise AnsibleFilterError(f"scaling policy target_value should be >= 10") + raise AnsibleFilterError("scaling policy target_value should be >= 10") - if service_metric == 'ALBRequestCountPerTarget': + if service_metric == "ALBRequestCountPerTarget": return autoscaling_policy if target_value > 90: - raise AnsibleFilterError(f"Utilization scaling policy target_value should be between 10 and 90") + raise AnsibleFilterError( + "Utilization scaling policy target_value should be between 10 and 90" + ) return autoscaling_policy @@ -98,7 +110,6 @@ class FilterModule: def filters(): return { # jinja2 overrides - 'as_ecs_service': as_ecs_service, - 'as_ecs_autoscaling': as_ecs_autoscaling + "as_ecs_service": as_ecs_service, + "as_ecs_autoscaling": as_ecs_autoscaling, } - diff --git a/ansible/library/filter_api_products.py b/ansible/library/filter_api_products.py index 9d151e1f..bc24f9bc 100644 --- a/ansible/library/filter_api_products.py +++ b/ansible/library/filter_api_products.py @@ -1,28 +1,32 @@ #!/usr/bin/python -from ansible.module_utils.basic import * +from ansible.module_utils.basic import AnsibleModule import json + def main(): fields = { "products": {"default": True, "type": "str"}, - "environment": {"default": True, "type": "str"} + "environment": {"default": True, "type": "str"}, } module = AnsibleModule(argument_spec=fields) - products_string = module.params['products'] - json_acceptable_string = products_string.replace("'", "\"") + products_string = module.params["products"] + json_acceptable_string = products_string.replace("'", '"') products = json.loads(json_acceptable_string) current_env = module.params["environment"] for product in products: - filtered_product = list(filter(lambda env: env == current_env, product["environments"])) + filtered_product = list( + filter(lambda env: env == current_env, product["environments"]) + ) product["environments"] = filtered_product module.params.update({"products": products}) - + module.exit_json(changed=True, meta=module.params) -if __name__ == '__main__': - main() \ No newline at end of file + +if __name__ == "__main__": + main() From 1fea38522244c43ea73f1c9cd9dd893771e0e0bd Mon Sep 17 00:00:00 2001 From: Sathiyakumar Ganesan Date: Mon, 11 May 2026 14:16:19 +0100 Subject: [PATCH 25/38] Refactor code for improved readability and consistency in calculate_version.py, scan-secrets.sh, and purge_incorrect_env_vars.py --- scripts/calculate_version.py | 6 +---- scripts/githooks/scan-secrets.sh | 13 ++++++++-- scripts/purge_incorrect_env_vars.py | 40 ++++++++++++++++------------- 3 files changed, 34 insertions(+), 25 deletions(-) diff --git a/scripts/calculate_version.py b/scripts/calculate_version.py index 56c8dc7a..35c84a5d 100644 --- a/scripts/calculate_version.py +++ b/scripts/calculate_version.py @@ -21,7 +21,6 @@ import git import semver - SCRIPT_LOCATION = os.path.join(os.path.dirname(os.path.abspath(__file__))) REPO_ROOT = os.path.abspath(os.path.join(SCRIPT_LOCATION, "..")) REPO = git.Repo(REPO_ROOT) @@ -67,11 +66,10 @@ def without_empty(commits): yield commits[-1] -def calculate_version(base_major=1, base_minor=0, base_revision=0, base_pre="alpha"): +def calculate_version(base_major=1, base_minor=0, base_pre="alpha"): """Calculates a semver based on commit history and special flags in commit messages""" major = base_major minor = base_minor - patch = base_revision pre = base_pre commits = get_versionable_commits(REPO) @@ -96,7 +94,6 @@ def calculate_version(base_major=1, base_minor=0, base_revision=0, base_pre="alp if major_incs: major += len(major_incs) minor = 0 - patch = 0 # If there are any +minor in commit messages, increment the counter # We only care about commits after the last major increment @@ -105,7 +102,6 @@ def calculate_version(base_major=1, base_minor=0, base_revision=0, base_pre="alp if minor_incs: minor += len(minor_incs) - patch = 0 # Now increment patch number for every commit since the last patch commits = list(itertools.takewhile(lambda c: not is_minor_inc(c), commits)) diff --git a/scripts/githooks/scan-secrets.sh b/scripts/githooks/scan-secrets.sh index be4b7914..5cca0510 100644 --- a/scripts/githooks/scan-secrets.sh +++ b/scripts/githooks/scan-secrets.sh @@ -34,6 +34,7 @@ function main() { dir="/workdir" cmd="$(get-cmd-to-run)" run-gitleaks-in-docker fi + return 0 } # Get Gitleaks command to execute and configuration. @@ -52,15 +53,20 @@ function get-cmd-to-run() { "staged-changes") cmd="protect --source $dir --verbose --staged" ;; + *) + echo "ERROR: Unknown check type: $check" >&2 + return 1 + ;; esac # Include base line file if it exists - if [ -f "$dir/scripts/config/.gitleaks-baseline.json" ]; then + if [[ -f "$dir/scripts/config/.gitleaks-baseline.json" ]]; then cmd="$cmd --baseline-path $dir/scripts/config/.gitleaks-baseline.json" fi # Include the config file cmd="$cmd --config $dir/scripts/config/gitleaks.toml" echo "$cmd" + return 0 } # Run Gitleaks natively. @@ -70,6 +76,7 @@ function run-gitleaks-natively() { # shellcheck disable=SC2086 gitleaks $cmd + return 0 } # Run Gitleaks in a Docker container. @@ -89,13 +96,15 @@ function run-gitleaks-in-docker() { --workdir $dir \ "$image" \ $cmd + return 0 } # ============================================================================== function is-arg-true() { + local arg="$1" - if [[ "$1" =~ ^(true|yes|y|on|1|TRUE|YES|Y|ON)$ ]]; then + if [[ "$arg" =~ ^(true|yes|y|on|1|TRUE|YES|Y|ON)$ ]]; then return 0 else return 1 diff --git a/scripts/purge_incorrect_env_vars.py b/scripts/purge_incorrect_env_vars.py index c076a9d8..0d166369 100644 --- a/scripts/purge_incorrect_env_vars.py +++ b/scripts/purge_incorrect_env_vars.py @@ -18,25 +18,27 @@ -t | --token= Auth token (get_token) -c --check Will show the data due to be deleted """ + import json import requests from docopt import docopt + def find_offending_entires(json_data, offending_str): - if check_mode: - print("*************************** Check mode *****************************") - print("* The following items would be deleted when run outside of check mode *") - for api in json_data: - for env in json_data[api]: - to_keep = [] - for line in json_data[api][env]: - if offending_str not in line: - to_keep.append(line) - else: - if check_mode: - print(" -", line) - json_data[api][env] = to_keep - return json_data + if check_mode: + print("*************************** Check mode *****************************") + print("* The following items would be deleted when run outside of check mode *") + for api in json_data: + for env in json_data[api]: + to_keep = [] + for line in json_data[api][env]: + if offending_str not in line: + to_keep.append(line) + elif check_mode: + print(" -", line) + json_data[api][env] = to_keep + return json_data + def post_new_data(new_json_data): url = f"https://api.enterprise.apigee.com/v1/organizations/{org_name}/environments/{env_name}/keyvaluemaps/{map_name}/entries/{entry_name}" @@ -46,7 +48,8 @@ def post_new_data(new_json_data): post_new_json_data = requests.post(url, json=format_new_json, headers=auth) print(post_new_json_data.text) - + + def main(args): global map_name, entry_name, token, check_mode input_value = str(args["--value"]) @@ -63,12 +66,13 @@ def main(args): url = f"https://api.enterprise.apigee.com/v1/organizations/{org_name}/environments/{env_name}/keyvaluemaps/{map_name}/entries/{entry_name}" resp = requests.get(url, headers=auth) - json_data = json.loads(json.loads(resp.text).get('value')) + json_data = json.loads(json.loads(resp.text).get("value")) new_json_data = find_offending_entires(json_data, input_value) if not check_mode: - post_new_data(new_json_data) + post_new_data(new_json_data) + if __name__ == "__main__": - main(docopt(__doc__, version="1")) + main(docopt(__doc__, version="1")) From 1737b63fa8f7373ef33e21f804ac76caaa9504fb Mon Sep 17 00:00:00 2001 From: Sathiyakumar Ganesan Date: Mon, 11 May 2026 14:30:16 +0100 Subject: [PATCH 26/38] Refactor prepend_dist_dir_to_spec_paths in validate_manifest.py for clarity and efficiency; simplify clean_env function in env_cleaner.py by removing unused parameters. --- .../models/ansible/validate_manifest.py | 19 ++++++++----------- env_cleaner.py | 7 +++---- 2 files changed, 11 insertions(+), 15 deletions(-) diff --git a/ansible/collections/ansible_collections/nhsd/apigee/plugins/module_utils/models/ansible/validate_manifest.py b/ansible/collections/ansible_collections/nhsd/apigee/plugins/module_utils/models/ansible/validate_manifest.py index f5c0bed4..93cee623 100644 --- a/ansible/collections/ansible_collections/nhsd/apigee/plugins/module_utils/models/ansible/validate_manifest.py +++ b/ansible/collections/ansible_collections/nhsd/apigee/plugins/module_utils/models/ansible/validate_manifest.py @@ -53,18 +53,15 @@ def check_service_name(cls, service_name, values): def prepend_dist_dir_to_spec_paths(cls, manifest, values): dist_dir = values.get("dist_dir") print(dist_dir) - if not dist_dir: - return manifest - changed = False - apigee = manifest["apigee"] - for env_dict in apigee["environments"]: - for spec_dict in env_dict["specs"]: - path = spec_dict.get("path") - if path is not None: - spec_dict["path"] = os.path.join(dist_dir, path) - changed = True + if dist_dir: + apigee = manifest["apigee"] + for env_dict in apigee["environments"]: + for spec_dict in env_dict["specs"]: + path = spec_dict.get("path") + if path is not None: + spec_dict["path"] = os.path.join(dist_dir, path) - return manifest if changed else manifest + return manifest @pydantic.validator("manifest") def check_namespacing(cls, manifest, values): diff --git a/env_cleaner.py b/env_cleaner.py index b9243350..42d03cb7 100644 --- a/env_cleaner.py +++ b/env_cleaner.py @@ -88,6 +88,7 @@ def clean_proxies( undeploy_only: bool = False, respect_prs: bool = False, ): + open_prs = set() if respect_prs: # Get open PRs, if there are any @@ -153,12 +154,11 @@ def clean_env( should_clean_products: bool = False, sandboxes_only: bool = False, dry_run: bool = False, - min_age: Optional[int] = None, undeploy_only: bool = False, respect_prs: bool = False, ): if should_clean_specs: - clean_specs(client, env, dry_run) + clean_specs(client, dry_run) if should_clean_proxies: clean_proxies( @@ -167,13 +167,12 @@ def clean_env( env, dry_run, sandboxes_only, - min_age, undeploy_only, respect_prs, ) if should_clean_products: - clean_products(client, env, dry_run) + clean_products(client, dry_run) if __name__ == "__main__": From 55390cf2fb01ac8474d350a3b8bf182c62dfb90b Mon Sep 17 00:00:00 2001 From: Sathiyakumar Ganesan Date: Mon, 11 May 2026 14:31:57 +0100 Subject: [PATCH 27/38] Remove unused min_age parameter from clean_specs function in env_cleaner.py --- env_cleaner.py | 1 - 1 file changed, 1 deletion(-) diff --git a/env_cleaner.py b/env_cleaner.py index 42d03cb7..59db4e96 100644 --- a/env_cleaner.py +++ b/env_cleaner.py @@ -188,7 +188,6 @@ def clean_env( should_clean_products=args["--products"], sandboxes_only=args["--sandbox-only"], dry_run=args["--dry-run"], - min_age=args["--min-age"], undeploy_only=args["--undeploy-only"], respect_prs=args["--respect-prs"], ) From 62a5bdf1018cd55c3b4543dce0988f3bb0d3d1af Mon Sep 17 00:00:00 2001 From: Sathiyakumar Ganesan Date: Mon, 11 May 2026 14:50:25 +0100 Subject: [PATCH 28/38] Add environment variable for service_name in apigee-build.yml check workspace step --- azure/common/apigee-build.yml | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/azure/common/apigee-build.yml b/azure/common/apigee-build.yml index befd8a7e..d7b8a8fb 100644 --- a/azure/common/apigee-build.yml +++ b/azure/common/apigee-build.yml @@ -51,10 +51,12 @@ jobs: steps: - bash: | - if [ -n "$(ls -A "$(Pipeline.Workspace)/s/$(service_name)" 2>/dev/null)" ]; then + if [ -n "$(ls -A "$(Pipeline.Workspace)/s/$service_name" 2>/dev/null)" ]; then echo "workspace directory is not empty!" exit 1 fi + env: + service_name: ${{ parameters.service_name }} displayName: "check workspace is clean" - bash: | From 3cc6eb5c79801bca06a95503007a80442a548173 Mon Sep 17 00:00:00 2001 From: Sathiyakumar Ganesan Date: Mon, 11 May 2026 14:52:33 +0100 Subject: [PATCH 29/38] Debug --- azure/common/apigee-build.yml | 4 +--- 1 file changed, 1 insertion(+), 3 deletions(-) diff --git a/azure/common/apigee-build.yml b/azure/common/apigee-build.yml index d7b8a8fb..9abcd5c5 100644 --- a/azure/common/apigee-build.yml +++ b/azure/common/apigee-build.yml @@ -51,12 +51,10 @@ jobs: steps: - bash: | - if [ -n "$(ls -A "$(Pipeline.Workspace)/s/$service_name" 2>/dev/null)" ]; then + if [ ! -z "$(ls -A \"$(Pipeline.Workspace)/s/$(service_name)\" 2>/dev/null)" ]; then echo "workspace directory is not empty!" exit 1 fi - env: - service_name: ${{ parameters.service_name }} displayName: "check workspace is clean" - bash: | From 212e21407d02f896b8cfe14b8798d198fc76d39a Mon Sep 17 00:00:00 2001 From: Sathiyakumar Ganesan Date: Tue, 12 May 2026 09:45:47 +0100 Subject: [PATCH 30/38] Refactor deploy-service.yml to remove redundant variable declarations and improve environment variable usage for clarity and maintainability. --- azure/templates/deploy-service.yml | 104 +++++++++++++++++------------ 1 file changed, 63 insertions(+), 41 deletions(-) diff --git a/azure/templates/deploy-service.yml b/azure/templates/deploy-service.yml index bbf024d5..32c861a8 100644 --- a/azure/templates/deploy-service.yml +++ b/azure/templates/deploy-service.yml @@ -49,15 +49,6 @@ parameters: - portal_api_requires_callback_url - spec_file -variables: - service_name: ${{ parameters.service_name }} - apigee_environment: ${{ parameters.apigee_environment }} - aws_account: ${{ parameters.aws_account }} - service_base_path: ${{ parameters.service_base_path }} - fully_qualified_service_name: ${{ parameters.fully_qualified_service_name }} - apigee_organization: ${{ parameters.apigee_organization }} - proxy_path: ${{ parameters.proxy_path }} - steps: # Warn user they are passing deprecated variables to the deployment pipeline - ${{ each variable_name in parameters._deprecated_pipeline_variables }}: @@ -70,12 +61,14 @@ steps: displayName: Warn ${{ variable_name }} is a deprecated pipeline parameter - bash: | ls -R - if [[ -f ecs-deploy-$(apigee_environment).yml ]] || [[ -f ecs-proxies-deploy.yml ]] || [[ -f ecs-proxies-deploy-sandbox.yml ]]; then + if [[ -f ecs-deploy-$apigee_environment.yml ]] || [[ -f ecs-proxies-deploy.yml ]] || [[ -f ecs-proxies-deploy-sandbox.yml ]]; then echo "##vso[task.setvariable variable=deploy_containers]true" else echo "##vso[task.setvariable variable=deploy_containers]false" fi workingDirectory: "$(SERVICE_DIR)" + env: + apigee_environment: ${{ parameters.apigee_environment }} displayName: "Check for ECS proxy definitions" - task: s3-cache-action@1 @@ -104,7 +97,11 @@ steps: - ${{ if parameters.proxy_path }}: - bash: | - echo "Deploying $(service_name) artifact $(SERVICE_ARTIFACT_NAME) as $(fully_qualified_service_name) to $(service_base_path) on $(ENV_URL)" + echo "Deploying $service_name artifact $(SERVICE_ARTIFACT_NAME) as $fully_qualified_service_name to $service_base_path on $(ENV_URL)" + env: + service_name: ${{ parameters.service_name }} + fully_qualified_service_name: ${{ parameters.fully_qualified_service_name }} + service_base_path: ${{ parameters.service_base_path }} displayName: "Deploy Info" - ${{ each pre_template_step in parameters.pre_template }}: @@ -131,10 +128,10 @@ steps: - bash: | set -euo pipefail - export SERVICE_NAME="$(service_name)" + export SERVICE_NAME="$service_name" export PROXIES_DIR="$(SERVICE_DIR)/proxies" - export SERVICE_BASE_PATH="$(service_base_path)" - export APIGEE_ENVIRONMENT="$(apigee_environment)" + export SERVICE_BASE_PATH="$service_base_path" + export APIGEE_ENVIRONMENT="$apigee_environment" export HOSTED_TARGET_CONNECTION_PATH_SUFFIX="$hosted_target_connection_path_suffix" export HOSTED_TARGET_HEALTHCHECK_SUFFIX="$hosted_target_healthcheck_suffix" export SOURCE_COMMIT_ID=$(Build.SourceVersion) @@ -144,7 +141,7 @@ steps: if [[ ! -z $ASSUMED_VERSION ]]; then export DEPLOYED_VERSION=$ASSUMED_VERSION else - export DEPLOYED_VERSION="$(fully_qualified_service_name)" + export DEPLOYED_VERSION="$fully_qualified_service_name" fi if [ -f $(SERVICE_DIR)/.build_env_vars ]; then @@ -157,11 +154,16 @@ steps: if [ -f $(SERVICE_DIR)/manifest.yml ]; then DIST_DIR=$(SERVICE_DIR) \ - PROXY_DIR=$(proxy_path) \ + PROXY_DIR=$proxy_path \ ANSIBLE_FORCE_COLOR=yes \ make --no-print-directory -C ansible add-apim-guids-policy fi env: + proxy_path: ${{ parameters.proxy_path }} + service_name: ${{ parameters.service_name }} + service_base_path: ${{ parameters.service_base_path }} + apigee_environment: ${{ parameters.apigee_environment }} + fully_qualified_service_name: ${{ parameters.fully_qualified_service_name }} hosted_target_connection_path_suffix: ${{ parameters.hosted_target_connection_path_suffix }} hosted_target_healthcheck_suffix: ${{ parameters.hosted_target_healthcheck_suffix }} displayName: Template proxies @@ -177,8 +179,8 @@ steps: proxy_vars_file="" - if [ -f $(SERVICE_DIR)/ecs-deploy-$(apigee_environment).yml ]; then - proxy_vars_file="$(SERVICE_DIR)/ecs-deploy-$(apigee_environment).yml" + if [ -f $(SERVICE_DIR)/ecs-deploy-$apigee_environment.yml ]; then + proxy_vars_file="$(SERVICE_DIR)/ecs-deploy-$apigee_environment.yml" else if [ -f $(SERVICE_DIR)/ecs-deploy-all.yml ]; then proxy_vars_file="$(SERVICE_DIR)/ecs-deploy-all.yml" @@ -188,22 +190,28 @@ steps: if [[ ! -z "${proxy_vars_file}" ]]; then source $(SERVICE_DIR)/.build_env_vars - deploy_role="deploy-$(apigee_environment)-${service_id}" + deploy_role="deploy-$apigee_environment-${service_id}" - account=$(aws_account) \ - SERVICE_BASE_PATH=$(service_base_path) \ - APIGEE_ENVIRONMENT=$(apigee_environment) \ + account=$aws_account \ + SERVICE_BASE_PATH=$service_base_path \ + APIGEE_ENVIRONMENT=$apigee_environment \ make --no-print-directory -C $(UTILS_DIR)/ansible create-api-deployment-pre-reqs fi echo "##vso[task.setvariable variable=PROXY_VARS_FILE]${proxy_vars_file}" echo "##vso[task.setvariable variable=DEPLOY_ROLE]${deploy_role}" + env: + apigee_environment: ${{ parameters.apigee_environment }} + service_base_path: ${{ parameters.service_base_path }} + aws_account: ${{ parameters.aws_account }} displayName: Create ECS Prerequisites condition: and(succeeded(), eq(variables['deploy_containers'], 'true')) - bash: | - echo "##vso[task.setvariable variable=SERVICE_BASE_PATH_FOR_CONDITION]$(service_base_path)" + echo "##vso[task.setvariable variable=SERVICE_BASE_PATH_FOR_CONDITION]$service_base_path" + env: + service_base_path: ${{ parameters.service_base_path }} displayName: Set SERVICE_BASE_PATH_FOR_CONDITION - bash: | @@ -212,18 +220,21 @@ steps: export CONTAINER_VARS_FILE="$SERVICE_DIR/ecs-proxies-containers.yml" source $(SERVICE_DIR)/.build_env_vars - export SERVICE_BASE_PATH="$(service_base_path)" + export SERVICE_BASE_PATH="$service_base_path" echo "SERVICE_BASE_PATH='${SERVICE_BASE_PATH}'" export ASSUMED_VERSION=$(echo $SERVICE_ARTIFACT_NAME | grep -E -o "v[0-9]+\.[0-9]+\.[0-9]+-?[a-z]*" || true | tail -1) - export DEPLOYED_VERSION=${ASSUMED_VERSION:-$(fully_qualified_service_name)} + export DEPLOYED_VERSION=${ASSUMED_VERSION:-$fully_qualified_service_name} - account=$(aws_account) \ + account=$aws_account \ CONTAINER_VARS_FILE="${CONTAINER_VARS_FILE}" \ SOURCE_COMMIT_ID="$(Build.SourceVersion)" \ RELEASE_RELEASEID="$(Build.BuildId)" \ make --no-print-directory -C $(UTILS_DIR)/ansible deploy-ecs-proxies-retag - + env: + service_base_path: ${{ parameters.service_base_path }} + aws_account: ${{ parameters.aws_account }} + fully_qualified_service_name: ${{ parameters.fully_qualified_service_name }} displayName: Retag ECS image condition: and(succeeded(), ne(variables['DEPLOY_ROLE'], ''), contains(variables['SERVICE_BASE_PATH_FOR_CONDITION'], 'canary'), not(contains(variables['SERVICE_BASE_PATH_FOR_CONDITION'], '-pr-'))) @@ -243,7 +254,7 @@ steps: if [[ ! -z $ASSUMED_VERSION ]]; then export DEPLOYED_VERSION=$ASSUMED_VERSION else - export DEPLOYED_VERSION="$(fully_qualified_service_name)" + export DEPLOYED_VERSION="$fully_qualified_service_name" fi @@ -258,15 +269,19 @@ steps: export use_ecs_tag=$TF_VAR_use_ecs_tag - account=$(aws_account) \ + account=$aws_account \ PROXY_VARS_FILE="${proxy_vars_file}" \ SOURCE_COMMIT_ID="$(Build.SourceVersion)" \ RELEASE_RELEASEID="$(Build.BuildId)" \ - SERVICE_BASE_PATH=$(service_base_path) \ - APIGEE_ENVIRONMENT=$(apigee_environment) \ + SERVICE_BASE_PATH=$service_base_path \ + APIGEE_ENVIRONMENT=$apigee_environment \ use_ecs_tag="${use_ecs_tag}" \ make --no-print-directory -C $(UTILS_DIR)/ansible deploy-ecs-proxies - + env: + aws_account: ${{ parameters.aws_account }} + service_base_path: ${{ parameters.service_base_path }} + apigee_environment: ${{ parameters.apigee_environment }} + fully_qualified_service_name: ${{ parameters.fully_qualified_service_name }} displayName: Deploy ECS proxies condition: and(succeeded(), ne(variables['DEPLOY_ROLE'], '')) @@ -274,18 +289,23 @@ steps: - bash: | set -euo pipefail - export PROXY_DIR="$(SERVICE_DIR)/proxies/$(proxy_path)" - export SERVICE_NAME="$(service_name)" - export FULLY_QUALIFIED_SERVICE_NAME="$(fully_qualified_service_name)" - export SERVICE_BASE_PATH="$(service_base_path)" + export PROXY_DIR="$(SERVICE_DIR)/proxies/$proxy_path" + export SERVICE_NAME="$service_name" + export FULLY_QUALIFIED_SERVICE_NAME="$fully_qualified_service_name" + export SERVICE_BASE_PATH="$service_base_path" export APIGEE_ACCESS_TOKEN="$(secret.AccessToken)" - export APIGEE_ENVIRONMENT="$(apigee_environment)" - export APIGEE_ORGANIZATION="nhsd-$(apigee_organization)" + export APIGEE_ENVIRONMENT="$apigee_environment" + export APIGEE_ORGANIZATION="nhsd-$apigee_organization" export PING="$ping" export ANSIBLE_FORCE_COLOR=yes - make --no-print-directory -C $(UTILS_DIR)/ansible deploy-apigee-proxy env: + service_name: ${{ parameters.service_name }} + fully_qualified_service_name: ${{ parameters.fully_qualified_service_name }} + service_base_path: ${{ parameters.service_base_path }} + apigee_environment: ${{ parameters.apigee_environment }} + apigee_organization: ${{ parameters.apigee_organization }} + proxy_path: ${{ parameters.proxy_path }} ping: ${{ parameters.ping }} displayName: Deploy proxy @@ -293,13 +313,15 @@ steps: set -euo pipefail export APIGEE_ACCESS_TOKEN="$(secret.AccessToken)" - export APIGEE_ENVIRONMENT="$(apigee_environment)" - export APIGEE_ORGANIZATION="nhsd-$(apigee_organization)" + export APIGEE_ENVIRONMENT="$apigee_environment" + export APIGEE_ORGANIZATION="nhsd-$apigee_organization" export ANSIBLE_FORCE_COLOR=yes export DIST_DIR=$(SERVICE_DIR) export PULL_REQUEST="$pr_label" make --no-print-directory -C $(UTILS_DIR)/ansible deploy-manifest env: + apigee_environment: ${{ parameters.apigee_environment }} + apigee_organization: ${{ parameters.apigee_organization }} pr_label: ${{ parameters.pr_label }} displayName: "Deploy Manifest" From 076124bf42de74b6ca293bd9b17f74472c8b6ae4 Mon Sep 17 00:00:00 2001 From: Sathiyakumar Ganesan Date: Tue, 12 May 2026 09:50:14 +0100 Subject: [PATCH 31/38] Fix formatting inconsistencies in deploy-stage.yml and update display names for clarity --- azure/common/deploy-stage.yml | 81 ++++++++++++++++------------------- 1 file changed, 37 insertions(+), 44 deletions(-) diff --git a/azure/common/deploy-stage.yml b/azure/common/deploy-stage.yml index 4951b60c..fdd45920 100644 --- a/azure/common/deploy-stage.yml +++ b/azure/common/deploy-stage.yml @@ -22,7 +22,7 @@ parameters: - name: fully_qualified_service_name - name: service_base_path - name: pr_label - default: '' + default: "" - name: secret_file_ids type: object - name: secret_ids @@ -123,7 +123,7 @@ stages: role: "auto-ops" profile: "apm_${{ parameters.aws_account }}" aws_account: "${{ parameters.aws_account }}" - + - bash: | pyversion="3.13" PATCH=$(curl -fsSL https://raw.githubusercontent.com/actions/python-versions/main/versions-manifest.json \ @@ -133,20 +133,13 @@ stages: echo "##vso[task.setvariable variable=PY_VER]$PATCH" echo "Resolved latest python version: $PATCH" echo "##vso[task.setvariable variable=LD_LIBRARY_PATH;]/agent/_work/_tool/Python/${PATCH}/x64/lib/" - displayName: 'Query and set python tool cache path' - + displayName: "Query and set python tool cache path" - task: UsePythonVersion@0 name: UsePy - displayName: 'Use Python 3.13' + displayName: "Use Python 3.13" inputs: - versionSpec: '$(PY_VER)' - - # - bash: | - # echo "Checking the python version in use to set LD_LIBRARY_PATH" - # echo "Python location: $(UsePy.pythonLocation)" - # echo "##vso[task.setvariable variable=LD_LIBRARY_PATH]$(UsePy.pythonLocation)/lib" - # displayName: 'Set LD_LIBRARY_PATH' + versionSpec: "$(PY_VER)" - template: "../components/set-facts.yml" parameters: @@ -173,27 +166,27 @@ stages: apigee_organization: ${{ parameters.apigee_organization }} - ${{ if parameters.notify }}: - - bash: | - if [[ -z $(NOTIFY_COMMIT_SHA) ]]; then - export NOTIFY_COMMIT_SHA=`echo "$(Build.SourceVersionMessage)" | cut -d' ' -f2` - echo "##vso[task.setvariable variable=NOTIFY_COMMIT_SHA]$NOTIFY_COMMIT_SHA" - else - echo "##[debug]Using already provided NOTIFY_COMMIT_SHA=$(NOTIFY_COMMIT_SHA)" - fi - displayName: Set NOTIFY_COMMIT_SHA + - bash: | + if [[ -z $(NOTIFY_COMMIT_SHA) ]]; then + export NOTIFY_COMMIT_SHA=`echo "$(Build.SourceVersionMessage)" | cut -d' ' -f2` + echo "##vso[task.setvariable variable=NOTIFY_COMMIT_SHA]$NOTIFY_COMMIT_SHA" + else + echo "##[debug]Using already provided NOTIFY_COMMIT_SHA=$(NOTIFY_COMMIT_SHA)" + fi + displayName: Set NOTIFY_COMMIT_SHA - - template: '../components/update-github-status.yml' - parameters: - state: pending - description: "Deploy to ${{ parameters.environment }} started" - environment: ${{ parameters.environment }} + - template: "../components/update-github-status.yml" + parameters: + state: pending + description: "Deploy to ${{ parameters.environment }} started" + environment: ${{ parameters.environment }} - ${{ each param in parameters }}: - - ${{ if not(containsValue(parameters._expose_blacklist, param.key)) }}: - - bash: | - set -euo pipefail - echo "##vso[task.setvariable variable=${{ upper(param.key) }}]${{ param.value }}" - displayName: Setting ${{ upper(param.key) }}=${{ param.value }} + - ${{ if not(containsValue(parameters._expose_blacklist, param.key)) }}: + - bash: | + set -euo pipefail + echo "##vso[task.setvariable variable=${{ upper(param.key) }}]${{ param.value }}" + displayName: Setting ${{ upper(param.key) }}=${{ param.value }} - bash: | set -euo pipefail @@ -261,7 +254,7 @@ stages: displayName: Check supplied names against API registry continueOnError: true - - template: '../templates/deploy-service.yml' + - template: "../templates/deploy-service.yml" parameters: service_name: ${{ parameters.service_name }} short_service_name: ${{ parameters.short_service_name }} @@ -282,7 +275,7 @@ stages: post_deploy: ${{ parameters.post_deploy }} ping: ${{ parameters.ping }} enable_monitoring: ${{ parameters.enable_monitoring }} - enable_status_monitoring : ${{ parameters.enable_status_monitoring }} + enable_status_monitoring: ${{ parameters.enable_status_monitoring }} portal_api_requires_callback_url: ${{ parameters.portal_api_requires_callback_url }} make_spec_visible: ${{ parameters.make_spec_visible }} friendly_api_name: ${{ parameters.friendly_api_name }} @@ -291,16 +284,16 @@ stages: aws_account: ${{ parameters.aws_account }} - ${{ if parameters.notify }}: - - template: '../components/update-github-status.yml' - parameters: - state: success - on_success: true - description: "Deploy to ${{ parameters.environment }} succeeded" - environment: ${{ parameters.environment }} + - template: "../components/update-github-status.yml" + parameters: + state: success + on_success: true + description: "Deploy to ${{ parameters.environment }} succeeded" + environment: ${{ parameters.environment }} - - template: '../components/update-github-status.yml' - parameters: - state: failure - on_failure: true - description: "Deploy to ${{ parameters.environment }} failed" - environment: ${{ parameters.environment }} + - template: "../components/update-github-status.yml" + parameters: + state: failure + on_failure: true + description: "Deploy to ${{ parameters.environment }} failed" + environment: ${{ parameters.environment }} From 611eaf6f470fd0fab17cc5d3b9dd6f03d29958d6 Mon Sep 17 00:00:00 2001 From: Sathiyakumar Ganesan Date: Tue, 12 May 2026 12:04:17 +0100 Subject: [PATCH 32/38] Refactor code for consistency and clarity across multiple files, including variable naming and dictionary initialization. Co-authored-by: Copilot --- .../plugins/action/add_policy_to_pre_flow.py | 2 +- .../plugins/module_utils/apigee_action.py | 4 +- .../module_utils/models/apigee/product.py | 10 +- .../module_utils/models/manifest/meta.py | 14 ++- .../nhsd/apigee/scripts/update_schema.py | 18 ++- .../models/manifest/test_product.py | 26 ++-- ansible/filter_plugins/apigee_helpers.py | 112 +++++++++--------- ansible/filter_plugins/ecs_service.py | 38 +++--- scripts/terraform_force_unlock.py | 12 +- scripts/test_pull_request_deployments.py | 21 +--- 10 files changed, 130 insertions(+), 127 deletions(-) diff --git a/ansible/collections/ansible_collections/nhsd/apigee/plugins/action/add_policy_to_pre_flow.py b/ansible/collections/ansible_collections/nhsd/apigee/plugins/action/add_policy_to_pre_flow.py index 92dbedbe..fc0de294 100644 --- a/ansible/collections/ansible_collections/nhsd/apigee/plugins/action/add_policy_to_pre_flow.py +++ b/ansible/collections/ansible_collections/nhsd/apigee/plugins/action/add_policy_to_pre_flow.py @@ -32,7 +32,7 @@ def run(self, tmp=None, task_vars=None): proxies_dir = args.dist_dir.joinpath( "proxies", args.proxy_dir, "apiproxy/proxies" ) - proxies_files = [f for f in proxies_dir.glob("*.xml")] + proxies_files = list(proxies_dir.glob("*.xml")) if len(proxies_files) != 1: return { diff --git a/ansible/collections/ansible_collections/nhsd/apigee/plugins/module_utils/apigee_action.py b/ansible/collections/ansible_collections/nhsd/apigee/plugins/module_utils/apigee_action.py index 85f05b67..de007242 100644 --- a/ansible/collections/ansible_collections/nhsd/apigee/plugins/module_utils/apigee_action.py +++ b/ansible/collections/ansible_collections/nhsd/apigee/plugins/module_utils/apigee_action.py @@ -4,10 +4,10 @@ class ApigeeAction(ansible.plugins.action.ActionBase): - def validate_args(self, Validator: type[pydantic.BaseModel]): + def validate_args(self, validator: type[pydantic.BaseModel]): """Returns two-length tuple of validated_args and errors dicts.""" try: - args = Validator(**self._task.args) + args = validator(**self._task.args) return args, {} except pydantic.ValidationError as e: return ( diff --git a/ansible/collections/ansible_collections/nhsd/apigee/plugins/module_utils/models/apigee/product.py b/ansible/collections/ansible_collections/nhsd/apigee/plugins/module_utils/models/apigee/product.py index 905c6dc7..1108c8d1 100644 --- a/ansible/collections/ansible_collections/nhsd/apigee/plugins/module_utils/models/apigee/product.py +++ b/ansible/collections/ansible_collections/nhsd/apigee/plugins/module_utils/models/apigee/product.py @@ -137,7 +137,7 @@ class ApigeeProduct(BaseModel): Union[ApigeeProductAttributeSpecial, ApigeeProductAttributeOther] ] = [{"name": "access", "value": "private"}] description: Optional[str] = None - displayName: Optional[str] = None + display_name: Optional[str] = None # Note: This value is manually inserted by apigee_environment # object that contains this product. So if you do not provide a @@ -206,8 +206,8 @@ def ensure_identity_service(cls, proxies, values): return [f"identity-service-{env}"] return proxies - @validator("displayName", always=True) - def validate_display_name(cls, displayName, values): - if not displayName: + @validator("display_name", always=True) + def validate_display_name(cls, display_name, values): + if not display_name: return values["name"] - return displayName + return display_name diff --git a/ansible/collections/ansible_collections/nhsd/apigee/plugins/module_utils/models/manifest/meta.py b/ansible/collections/ansible_collections/nhsd/apigee/plugins/module_utils/models/manifest/meta.py index d66e8931..d6210af3 100644 --- a/ansible/collections/ansible_collections/nhsd/apigee/plugins/module_utils/models/manifest/meta.py +++ b/ansible/collections/ansible_collections/nhsd/apigee/plugins/module_utils/models/manifest/meta.py @@ -9,7 +9,6 @@ _REGISTRY_DATA = {} - class ManifestMetaApi(pydantic.BaseModel): name: pydantic.constr(regex=r"^[a-z][a-z0-9]*(-[a-z0-9]+)*$") id: typing.Optional[pydantic.UUID4] = pydantic.Field( @@ -29,7 +28,7 @@ def dict(self, **kwargs): { "guid": str(native["guid"]), "spec_guids": [str(guid) for guid in spec_guids], - } + } ) return native @@ -58,7 +57,9 @@ def validate_guid(cls, guid, values): guid = _REGISTRY_DATA[name]["guid"] registered_guid = _REGISTRY_DATA[name]["guid"] if str(guid) != registered_guid: - raise ValueError(f"Supplied guid {guid} does not match registered guid {registered_guid}") + raise ValueError( + f"Supplied guid {guid} does not match registered guid {registered_guid}" + ) return guid @pydantic.validator("spec_guids") @@ -78,11 +79,12 @@ def validate_spec_guids(cls, spec_guids, values): if str(spec_guid) not in registered_spec_guids: invalid.append(str(spec_guid)) if len(invalid) > 0: - raise ValueError(f"Supplied spec_guids {invalid} do not match registered spec_guids {registered_spec_guids}") + raise ValueError( + f"Supplied spec_guids {invalid} do not match registered spec_guids {registered_spec_guids}" + ) return spec_guids - class ManifestMeta(pydantic.BaseModel): schema_version: pydantic.constr(regex=r"[1-9][0-9]*(\.[0-9]+){0,2}") api: ManifestMetaApi @@ -91,7 +93,7 @@ class ManifestMeta(pydantic.BaseModel): def validate_schema_version(cls, schema_version): semantic_parts = schema_version.split(".") - MAJOR, MINOR, PATCH = [int(x) for x in SCHEMA_VERSION.split(".")] + MAJOR, _, _ = [int(x) for x in SCHEMA_VERSION.split(".")] major = int(semantic_parts[0]) # Checking against minor/patch would not allow to us deploy diff --git a/ansible/collections/ansible_collections/nhsd/apigee/scripts/update_schema.py b/ansible/collections/ansible_collections/nhsd/apigee/scripts/update_schema.py index 2d5e56f5..9ed1d3ef 100644 --- a/ansible/collections/ansible_collections/nhsd/apigee/scripts/update_schema.py +++ b/ansible/collections/ansible_collections/nhsd/apigee/scripts/update_schema.py @@ -12,7 +12,7 @@ Manifest, ) -SCHEMA_VERSION_REGEX = re.compile(r"^([1-9][0-9]*)\.([0-9]+)\.([0-9]+)$") +SCHEMA_VERSION_REGEX = re.compile(r"^([1-9]\d*)\.(\d+)\.(\d+)$") class SchemaString: @@ -61,10 +61,18 @@ def __le__(self, other): return self < other or self == other def __gt__(self, other): - return not self <= other + return (self.major, self.minor, self.patch) > ( + other.major, + other.minor, + other.patch, + ) def __ge__(self, other): - return not self < other + return (self.major, self.minor, self.patch) >= ( + other.major, + other.minor, + other.patch, + ) def valid_increments(self): return [ @@ -86,7 +94,7 @@ def main(): schema_dir_glob = relative_schema_dir.glob("v*.json") SCHEMA_VERSION = SchemaString("1.0.0") - SCHEMA_FILE_NAME_PATTERN = re.compile(r"v([1-9][0-9]*\.[0-9]+\.[0-9]+)\.json$") + SCHEMA_FILE_NAME_PATTERN = re.compile(r"v([1-9]\d*\.\d+\.\d+)\.json$") for schema_file in schema_dir_glob: match = re.match(SCHEMA_FILE_NAME_PATTERN, schema_file.name) schema_version = SchemaString(match.group(1)) @@ -105,7 +113,7 @@ def main(): fromfile=str(SCHEMA_VERSION), tofile=str(UPDATED_SCHEMA_VERSION), ) - deltas = [delta for delta in deltas] + deltas = list(deltas) if not deltas: raise ValueError( diff --git a/ansible/collections/ansible_collections/nhsd/apigee/tests/unit/plugins/module_utils/models/manifest/test_product.py b/ansible/collections/ansible_collections/nhsd/apigee/tests/unit/plugins/module_utils/models/manifest/test_product.py index 17ad7338..b5803472 100644 --- a/ansible/collections/ansible_collections/nhsd/apigee/tests/unit/plugins/module_utils/models/manifest/test_product.py +++ b/ansible/collections/ansible_collections/nhsd/apigee/tests/unit/plugins/module_utils/models/manifest/test_product.py @@ -30,7 +30,7 @@ def _make_product_dict(name, environment="internal-dev", approval_type="auto"): @pytest.mark.parametrize( - "env,initial_approvalType,final_approvalType", + "env,initial_approvaltype,final_approvaltype", [ ("prod", "auto", "manual"), ("prod", "manual", "manual"), @@ -38,18 +38,18 @@ def _make_product_dict(name, environment="internal-dev", approval_type="auto"): ("int", "manual", "manual"), ], ) -def test_prod_cannot_have_auto_approvalType_on_products( - env, initial_approvalType, final_approvalType +def test_prod_cannot_have_auto_approvaltype_on_products( + env, initial_approvaltype, final_approvaltype ): product_dict = _make_product_dict( - "test-service", environment=env, approval_type=initial_approvalType + "test-service", environment=env, approval_type=initial_approvaltype ) product = ApigeeProduct(**product_dict) - assert product.approvalType == final_approvalType + assert product.approvalType == final_approvaltype @pytest.mark.parametrize( - "name,env,initial_approvalType,final_approvalType", + "name,env,initial_approvaltype,final_approvaltype", [ ("canary-api", "prod", "auto", "auto"), ("canary-api", "prod", "manual", "manual"), @@ -58,13 +58,13 @@ def test_prod_cannot_have_auto_approvalType_on_products( ], ) def test_manual_approval_exception_list_on_prod( - name, env, initial_approvalType, final_approvalType + name, env, initial_approvaltype, final_approvaltype ): product_dict = _make_product_dict( - name, environment=env, approval_type=initial_approvalType + name, environment=env, approval_type=initial_approvaltype ) product = ApigeeProduct(**product_dict) - assert product.approvalType == final_approvalType + assert product.approvalType == final_approvaltype def test_ratelimiting_product_attribute_initialized_with_dict_or_string(): @@ -75,15 +75,13 @@ def test_ratelimiting_product_attribute_initialized_with_dict_or_string(): "spikeArrest": {"ratelimit": "30000pm"}, # 5000 tps } attr_dict = {product_dict["name"]: ratelimiting_dict} - + # Init with dict-like - product_dict["attributes"].append( - {"name": "ratelimiting", "value": attr_dict} - ) + product_dict["attributes"].append({"name": "ratelimiting", "value": attr_dict}) product1 = ApigeeProduct(**product_dict) # Assert ratelimiting attribute gets serialized to a string - assert type(product1.attributes[-1].value) == str + assert isinstance(product1.attributes[-1].value, str) # Init with attribute values already a string attr_str = json.dumps(attr_dict) diff --git a/ansible/filter_plugins/apigee_helpers.py b/ansible/filter_plugins/apigee_helpers.py index c44117a8..993c1a2f 100644 --- a/ansible/filter_plugins/apigee_helpers.py +++ b/ansible/filter_plugins/apigee_helpers.py @@ -4,20 +4,20 @@ def apigee_apps_to_product_map(apps_list: List[dict], product_filter: str = None): - result = dict() + result = {} not_matched = [] for app in apps_list: - credentials = app.get('credentials', []) + credentials = app.get("credentials", []) for cred in credentials: - api_products = cred.get('apiProducts', []) + api_products = cred.get("apiProducts", []) for product in api_products: - api_product = product['apiproduct'] + api_product = product["apiproduct"] if product_filter and not re.match(product_filter, api_product): not_matched.append(api_product) continue @@ -25,8 +25,8 @@ def apigee_apps_to_product_map(apps_list: List[dict], product_filter: str = None if api_product not in result: result[api_product] = [] - company_exists = "companyName" in app.keys() - developer_exists = "developerId" in app.keys() + company_exists = "companyName" in app + developer_exists = "developerId" in app if developer_exists and not company_exists: owner = app["developerId"] elif company_exists and not developer_exists: @@ -35,36 +35,38 @@ def apigee_apps_to_product_map(apps_list: List[dict], product_filter: str = None raise RuntimeError(f"Invalid owner for app {app['appId']}") result[api_product].append( - dict( - appId=app["appId"], - appName=app["name"], - owner=owner, - ownerEndpoint="companies" if company_exists else "developers", - consumerKey=cred["consumerKey"], - apiproduct=api_product - ) + { + "appId": app["appId"], + "appName": app["name"], + "owner": owner, + "ownerEndpoint": ( + "companies" if company_exists else "developers" + ), + "consumerKey": cred["consumerKey"], + "apiproduct": api_product, + } ) for product in sorted(set(not_matched)): - print(f'did not match: {product}') + print(f"did not match: {product}") return result def product_app_mapping_to_owner_display(dev_id_to_email: dict, product_app: dict): - if product_app['ownerEndpoint'] == 'developers': - return dev_id_to_email[product_app['owner']] + if product_app["ownerEndpoint"] == "developers": + return dev_id_to_email[product_app["owner"]] else: - return product_app['owner'] + return product_app["owner"] def apigee_products_to_api_map(products: List[dict], proxy_filter: str = None): - result = dict() + result = {} not_matched = [] for product in products: - proxies = product.get('proxies', []) + proxies = product.get("proxies", []) for proxy in proxies: @@ -75,36 +77,41 @@ def apigee_products_to_api_map(products: List[dict], proxy_filter: str = None): if proxy not in result: result[proxy] = [] - result[proxy].append(product['name']) + result[proxy].append(product["name"]) for proxy in sorted(set(not_matched)): - print(f'did not match: {proxy}') + print(f"did not match: {proxy}") return result def apigee_remove_proxy_from_product(product: dict, proxy_to_remove): - product["proxies"] = [ - p for p in product.get("proxies", []) - if p != proxy_to_remove - ] + product["proxies"] = [p for p in product.get("proxies", []) if p != proxy_to_remove] return product def apigee_team_to_admin(team): - return next((attr.get('value') for attr in team['attributes'] if attr['name'] == 'ADMIN_EMAIL'), None) + return next( + ( + attr.get("value") + for attr in team["attributes"] + if attr["name"] == "ADMIN_EMAIL" + ), + None, + ) def apigee_teams_map(team_members: List[dict], teams: List[dict]): from collections import defaultdict + joined = defaultdict(dict) for item in team_members + teams: - joined[item['name']].update(item) + joined[item["name"]].update(item) full_teams = list(joined.values()) return { - team['name']: { + team["name"]: { "contact": apigee_team_to_admin(team), - "members": team['members'] + "members": team["members"], } for team in full_teams } @@ -116,19 +123,20 @@ def apigee_teams_to_point_of_contact(teams: List[dict]): def apigee_teams_to_members(teams: List[dict]): - return { - team['owner']: team.get('members', []) for team in teams - } + return {team["owner"]: team.get("members", []) for team in teams} def apigee_product_developers( - product_app_map: dict, dev_id_to_email: dict, teams_map: dict, product_filter: str = None + product_app_map: dict, + dev_id_to_email: dict, + teams_map: dict, + product_filter: str = None, ): if not product_app_map: - raise ValueError('product_app_map not set') + raise ValueError("product_app_map not set") if not dev_id_to_email: - raise ValueError('dev_id_to_email not set') + raise ValueError("dev_id_to_email not set") teams_map = teams_map or {} result = {} @@ -139,22 +147,18 @@ def apigee_product_developers( result[product_name] = [] for app in apps: - entry = { - "app": app["appName"] - } + entry = {"app": app["appName"]} - if app['ownerEndpoint'] == 'companies': + if app["ownerEndpoint"] == "companies": team = teams_map.get(app.get("owner"), {}) - entry['developer'] = team['contact'] - entry['team'] = team['members'] + entry["developer"] = team["contact"] + entry["team"] = team["members"] else: - entry['developer'] = dev_id_to_email[app["owner"]] + entry["developer"] = dev_id_to_email[app["owner"]] result[product_name].append(entry) - result = { - key: sorted(apps, key=lambda x: x['app']) for key, apps in result.items() - } + result = {key: sorted(apps, key=lambda x: x["app"]) for key, apps in result.items()} return result @@ -165,12 +169,12 @@ class FilterModule: def filters(): return { # jinja2 overrides - 'apigee_apps_to_product_map': apigee_apps_to_product_map, - 'apigee_products_to_api_map': apigee_products_to_api_map, - 'apigee_remove_proxy_from_product': apigee_remove_proxy_from_product, - 'apigee_teams_to_point_of_contact': apigee_teams_to_point_of_contact, - 'apigee_teams_to_members': apigee_teams_to_members, - 'apigee_teams_map': apigee_teams_map, - 'apigee_product_developers': apigee_product_developers, - 'product_app_mapping_to_owner_display': product_app_mapping_to_owner_display + "apigee_apps_to_product_map": apigee_apps_to_product_map, + "apigee_products_to_api_map": apigee_products_to_api_map, + "apigee_remove_proxy_from_product": apigee_remove_proxy_from_product, + "apigee_teams_to_point_of_contact": apigee_teams_to_point_of_contact, + "apigee_teams_to_members": apigee_teams_to_members, + "apigee_teams_map": apigee_teams_map, + "apigee_product_developers": apigee_product_developers, + "product_app_mapping_to_owner_display": product_app_mapping_to_owner_display, } diff --git a/ansible/filter_plugins/ecs_service.py b/ansible/filter_plugins/ecs_service.py index 695326ff..b1f93676 100644 --- a/ansible/filter_plugins/ecs_service.py +++ b/ansible/filter_plugins/ecs_service.py @@ -2,18 +2,18 @@ from ansible.plugins.filter.core import combine from collections import Counter -_container_defaults = dict( - cpu=0, - port=9000, - protocol="TCP", - environment=[], - mountPoints=[], - secrets=[], - expose=True, - essential=True, - health_check=dict(matcher="200", path="/"), - lb_protocol="HTTP", -) +_container_defaults = { + "cpu": 0, + "port": 9000, + "protocol": "TCP", + "environment": [], + "mountPoints": [], + "secrets": [], + "expose": True, + "essential": True, + "health_check": {"matcher": "200", "path": "/"}, + "lb_protocol": "HTTP", +} def as_ecs_service(docker_service): @@ -54,13 +54,13 @@ def as_ecs_service(docker_service): return ecs_service -_app_scaling_defaults = dict( - service_metric="", - target_value=0, - scale_in_cooldown=300, - scale_out_cooldown=60, - enabled=False, -) +_app_scaling_defaults = { + "service_metric": "", + "target_value": 0, + "scale_in_cooldown": 300, + "scale_out_cooldown": 60, + "enabled": False, +} _ecs_service_metrics = ( "ALBRequestCountPerTarget", diff --git a/scripts/terraform_force_unlock.py b/scripts/terraform_force_unlock.py index 7e631f97..9cbf1ef9 100644 --- a/scripts/terraform_force_unlock.py +++ b/scripts/terraform_force_unlock.py @@ -46,14 +46,14 @@ def main(min_age_hr, key_prefix, table_name, profile): filter_expr = "begins_with(#n0, :v0) AND attribute_exists(#n1)" - ExpressionAttributeNames = {"#n0": "LockID", "#n1": "Info"} - ExpressionAttributeValues = { + expression_attribute_names = {"#n0": "LockID", "#n1": "Info"} + expression_attribute_values = { ":v0": key_prefix, } items = terraform_lock_table.scan( FilterExpression=filter_expr, - ExpressionAttributeNames=ExpressionAttributeNames, - ExpressionAttributeValues=ExpressionAttributeValues, + ExpressionAttributeNames=expression_attribute_names, + ExpressionAttributeValues=expression_attribute_values, ) total_items = items["Items"] @@ -61,8 +61,8 @@ def main(min_age_hr, key_prefix, table_name, profile): while "LastEvaluatedKey" in items: items = terraform_lock_table.scan( FilterExpression=filter_expr, - ExpressionAttributeNames=ExpressionAttributeNames, - ExpressionAttributeValues=ExpressionAttributeValues, + ExpressionAttributeNames=expression_attribute_names, + ExpressionAttributeValues=expression_attribute_values, ExclusiveStartKey=items["LastEvaluatedKey"], ) total_items.extend(items["Items"]) diff --git a/scripts/test_pull_request_deployments.py b/scripts/test_pull_request_deployments.py index 39d56074..ed861dfb 100644 --- a/scripts/test_pull_request_deployments.py +++ b/scripts/test_pull_request_deployments.py @@ -3,13 +3,8 @@ from multiprocessing import Process from trigger_pipelines import AzureDevOps - PULL_REQUEST_PIPELINES = { - "canary-api": { - "build": 222, - "pr": 223, - "branch": "refs/heads/main" - } + "canary-api": {"build": 222, "pr": 223, "branch": "refs/heads/main"} } @@ -19,17 +14,10 @@ def trigger_pipelines(pipeline_ids: dict, service: str): service=service, pipeline_type="build", pipeline_id=pipeline_ids["build"], - pipeline_branch=pipeline_ids["branch"] + pipeline_branch=pipeline_ids["branch"], ) if build_status != "succeeded": sys.exit(1) - return - # azure_dev_ops.run_pipeline( - # service=service, - # pipeline_type="pr", - # pipeline_id=pipeline_ids["pr"], - # pipeline_branch=pipeline_ids["branch"] - # ) def main(): @@ -37,7 +25,10 @@ def main(): for service, pipeline_ids in PULL_REQUEST_PIPELINES.items(): process = Process( target=trigger_pipelines, - args=(pipeline_ids, service,) + args=( + pipeline_ids, + service, + ), ) process.start() jobs.append(process) From 8524fba2954722746686c205d47ed46662ad588b Mon Sep 17 00:00:00 2001 From: Sathiyakumar Ganesan Date: Tue, 12 May 2026 12:09:58 +0100 Subject: [PATCH 33/38] Update s3-cache-action version to 1.3.4 in multiple YAML files for consistency --- azure/cleanup-ecs-pr-proxies.yml | 2 +- azure/cleanup-pr-portal-apis-and-specs.yml | 2 +- azure/common/apigee-build.yml | 2 +- azure/components/remove-target-proxy.yml | 111 ++++++++++----------- azure/templates/deploy-service.yml | 2 +- 5 files changed, 59 insertions(+), 60 deletions(-) diff --git a/azure/cleanup-ecs-pr-proxies.yml b/azure/cleanup-ecs-pr-proxies.yml index 5e2b7f00..bbe10d47 100644 --- a/azure/cleanup-ecs-pr-proxies.yml +++ b/azure/cleanup-ecs-pr-proxies.yml @@ -52,7 +52,7 @@ jobs: tfenv use 1.2.3 displayName: setup terraform - - task: s3-cache-action@1 + - task: s3-cache-action@1.3.4 inputs: key: poetry | utils | poetry.lock location: ".venv" diff --git a/azure/cleanup-pr-portal-apis-and-specs.yml b/azure/cleanup-pr-portal-apis-and-specs.yml index d636fbdc..50c0afab 100644 --- a/azure/cleanup-pr-portal-apis-and-specs.yml +++ b/azure/cleanup-pr-portal-apis-and-specs.yml @@ -42,7 +42,7 @@ jobs: tfenv use 0.14.6 displayName: setup terraform - - task: s3-cache-action@1 + - task: s3-cache-action@1.3.4 inputs: key: poetry | utils | poetry.lock location: ".venv" diff --git a/azure/common/apigee-build.yml b/azure/common/apigee-build.yml index 9abcd5c5..294326df 100644 --- a/azure/common/apigee-build.yml +++ b/azure/common/apigee-build.yml @@ -203,7 +203,7 @@ jobs: - checkout: common path: "s/${{ parameters.service_name }}/utils" - - task: s3-cache-action@1 + - task: s3-cache-action@1.3.4 inputs: key: poetry | $(System.DefaultWorkingDirectory) | ${{ parameters.service_name }}/utils/poetry.lock location: "${{ parameters.service_name }}/utils/.venv" diff --git a/azure/components/remove-target-proxy.yml b/azure/components/remove-target-proxy.yml index 36251c9f..0708eaf4 100644 --- a/azure/components/remove-target-proxy.yml +++ b/azure/components/remove-target-proxy.yml @@ -27,7 +27,7 @@ parameters: stages: - stage: approve_then_remove_target_proxy - displayName: Find and remove all components for ${{ parameters.service_name }} proxy in ${{ parameters.apigee_org }} org + displayName: Find and remove all components for ${{ parameters.service_name }} proxy in ${{ parameters.apigee_org }} org dependsOn: [] jobs: - deployment: build @@ -39,66 +39,65 @@ stages: workspace: clean: all strategy: - runOnce: - deploy: - steps: - - checkout: self + runOnce: + deploy: + steps: + - checkout: self - - template: ./print-aws-info.yml + - template: ./print-aws-info.yml - - task: s3-cache-action@1 - inputs: - key: poetry | utils | poetry.lock - location: ".venv" - debug: true - displayName: cache utils pre-requisites + - task: s3-cache-action@1.3.4 + inputs: + key: poetry | utils | poetry.lock + location: ".venv" + debug: true + displayName: cache utils pre-requisites - - bash: make install - displayName: Install project dependencies + - bash: make install + displayName: Install project dependencies - - template: ./aws-assume-role.yml - parameters: - role: auto-ops - aws_account: ${{ parameters.aws_account }} - profile: ${{ parameters.aws_profile }} + - template: ./aws-assume-role.yml + parameters: + role: auto-ops + aws_account: ${{ parameters.aws_account }} + profile: ${{ parameters.aws_profile }} - - bash: | - tfenv use 0.14.6 - displayName: setup terraform + - bash: | + tfenv use 0.14.6 + displayName: setup terraform - - template: ./get-aws-secrets-and-ssm-params.yml - parameters: - secret_ids: - - ${{ parameters.aws_account }}/azure-devops/apigee-${{ parameters.apigee_org }}/APIGEE_OTP_KEY - - ${{ parameters.aws_account }}/azure-devops/apigee-${{ parameters.apigee_org }}/APIGEE_PASSWORD - config_ids: - - /${{ parameters.aws_account }}/azure-devops/apigee-${{ parameters.apigee_org }}/APIGEE_USERNAME - aws_account: ${{ parameters.aws_account }} + - template: ./get-aws-secrets-and-ssm-params.yml + parameters: + secret_ids: + - ${{ parameters.aws_account }}/azure-devops/apigee-${{ parameters.apigee_org }}/APIGEE_OTP_KEY + - ${{ parameters.aws_account }}/azure-devops/apigee-${{ parameters.apigee_org }}/APIGEE_PASSWORD + config_ids: + - /${{ parameters.aws_account }}/azure-devops/apigee-${{ parameters.apigee_org }}/APIGEE_USERNAME + aws_account: ${{ parameters.aws_account }} - - bash: | - export MFA_CODE=`oathtool --totp -b "$(APIGEE_OTP_KEY)"` - # basic auth value here is an apigee public value .. https://docs.apigee.com/api-platform/system-administration/management-api-tokens (not secret) - curl -X POST https://${{ parameters._auth_server[parameters.apigee_org] }}/oauth/token \ - -H "Content-Type: application/x-www-form-urlencoded" \ - -H "Accept: application/json;charset=utf-8" \ - -H "Authorization: Basic ZWRnZWNsaTplZGdlY2xpc2VjcmV0" \ - -d "username=$(APIGEE_USERNAME)&password=$(APIGEE_PASSWORD)&mfa_token=${MFA_CODE}&grant_type=password" | jq .access_token > .token - # Set token into variable - echo "##vso[task.setvariable variable=secret.${{ upper(parameters.apigee_org) }}_API_ACCESS_TOKEN;issecret=true]`cat .token`" - displayName: Get access token + - bash: | + export MFA_CODE=`oathtool --totp -b "$(APIGEE_OTP_KEY)"` + # basic auth value here is an apigee public value .. https://docs.apigee.com/api-platform/system-administration/management-api-tokens (not secret) + curl -X POST https://${{ parameters._auth_server[parameters.apigee_org] }}/oauth/token \ + -H "Content-Type: application/x-www-form-urlencoded" \ + -H "Accept: application/json;charset=utf-8" \ + -H "Authorization: Basic ZWRnZWNsaTplZGdlY2xpc2VjcmV0" \ + -d "username=$(APIGEE_USERNAME)&password=$(APIGEE_PASSWORD)&mfa_token=${MFA_CODE}&grant_type=password" | jq .access_token > .token + # Set token into variable + echo "##vso[task.setvariable variable=secret.${{ upper(parameters.apigee_org) }}_API_ACCESS_TOKEN;issecret=true]`cat .token`" + displayName: Get access token - - bash: | - set -euo pipefail - export SERVICE_NAME="${{ parameters.service_name }}" - export SERVICE_NAME_SHORT="${{ parameters.service_name_short }}" - export APIGEE_ENVIRONMENT="${{ parameters.environment }}" - export APIGEE_ORG="nhsd-${{ parameters.apigee_org }}" - export APIGEE_ACCESS_TOKEN="$(secret.${{ parameters.apigee_org }}_API_ACCESS_TOKEN)" - export AWS_ACCOUNT="${{ parameters.aws_account }}" - export AWS_PROFILE="${{ parameters.aws_profile }}" - export has_ecs_backend="${{ parameters.has_ecs_backend }}" - export remove_ecr_build_role="${{ parameters.remove_ecr_build_role }}" - export force_remove_from_apps="${{ parameters.force_remove_from_apps }}" - ANSIBLE_FORCE_COLOR=yes make -C ansible remove-target-proxy - displayName: "Cleanup target proxy" - + - bash: | + set -euo pipefail + export SERVICE_NAME="${{ parameters.service_name }}" + export SERVICE_NAME_SHORT="${{ parameters.service_name_short }}" + export APIGEE_ENVIRONMENT="${{ parameters.environment }}" + export APIGEE_ORG="nhsd-${{ parameters.apigee_org }}" + export APIGEE_ACCESS_TOKEN="$(secret.${{ parameters.apigee_org }}_API_ACCESS_TOKEN)" + export AWS_ACCOUNT="${{ parameters.aws_account }}" + export AWS_PROFILE="${{ parameters.aws_profile }}" + export has_ecs_backend="${{ parameters.has_ecs_backend }}" + export remove_ecr_build_role="${{ parameters.remove_ecr_build_role }}" + export force_remove_from_apps="${{ parameters.force_remove_from_apps }}" + ANSIBLE_FORCE_COLOR=yes make -C ansible remove-target-proxy + displayName: "Cleanup target proxy" diff --git a/azure/templates/deploy-service.yml b/azure/templates/deploy-service.yml index 32c861a8..52f0f198 100644 --- a/azure/templates/deploy-service.yml +++ b/azure/templates/deploy-service.yml @@ -71,7 +71,7 @@ steps: apigee_environment: ${{ parameters.apigee_environment }} displayName: "Check for ECS proxy definitions" - - task: s3-cache-action@1 + - task: s3-cache-action@1.3.4 inputs: key: poetry | utils | $(UTILS_DIR)/poetry.lock location: "$(UTILS_DIR)/.venv" From 243b0816ab265d537fb52a85755fff8bac1b1b5a Mon Sep 17 00:00:00 2001 From: Sathiyakumar Ganesan Date: Tue, 12 May 2026 12:33:26 +0100 Subject: [PATCH 34/38] Update display_name field to use alias displayName for consistency with API standards --- .../nhsd/apigee/plugins/module_utils/models/apigee/product.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/ansible/collections/ansible_collections/nhsd/apigee/plugins/module_utils/models/apigee/product.py b/ansible/collections/ansible_collections/nhsd/apigee/plugins/module_utils/models/apigee/product.py index 1108c8d1..70766c46 100644 --- a/ansible/collections/ansible_collections/nhsd/apigee/plugins/module_utils/models/apigee/product.py +++ b/ansible/collections/ansible_collections/nhsd/apigee/plugins/module_utils/models/apigee/product.py @@ -137,7 +137,7 @@ class ApigeeProduct(BaseModel): Union[ApigeeProductAttributeSpecial, ApigeeProductAttributeOther] ] = [{"name": "access", "value": "private"}] description: Optional[str] = None - display_name: Optional[str] = None + display_name: Optional[str] = Field(None, alias="displayName") # Note: This value is manually inserted by apigee_environment # object that contains this product. So if you do not provide a From dfa5be4a7cf3ef5fa3cf966b5b3a1309e1bcfcaf Mon Sep 17 00:00:00 2001 From: Sathiyakumar Ganesan Date: Tue, 12 May 2026 15:53:40 +0100 Subject: [PATCH 35/38] Refactor display_name field to displayName for consistency with API standards Co-authored-by: Copilot --- .../plugins/module_utils/models/apigee/product.py | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/ansible/collections/ansible_collections/nhsd/apigee/plugins/module_utils/models/apigee/product.py b/ansible/collections/ansible_collections/nhsd/apigee/plugins/module_utils/models/apigee/product.py index 70766c46..905c6dc7 100644 --- a/ansible/collections/ansible_collections/nhsd/apigee/plugins/module_utils/models/apigee/product.py +++ b/ansible/collections/ansible_collections/nhsd/apigee/plugins/module_utils/models/apigee/product.py @@ -137,7 +137,7 @@ class ApigeeProduct(BaseModel): Union[ApigeeProductAttributeSpecial, ApigeeProductAttributeOther] ] = [{"name": "access", "value": "private"}] description: Optional[str] = None - display_name: Optional[str] = Field(None, alias="displayName") + displayName: Optional[str] = None # Note: This value is manually inserted by apigee_environment # object that contains this product. So if you do not provide a @@ -206,8 +206,8 @@ def ensure_identity_service(cls, proxies, values): return [f"identity-service-{env}"] return proxies - @validator("display_name", always=True) - def validate_display_name(cls, display_name, values): - if not display_name: + @validator("displayName", always=True) + def validate_display_name(cls, displayName, values): + if not displayName: return values["name"] - return display_name + return displayName From ce6bb81f0b24e61991b57387883aae564bdbfd83 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Thu, 14 May 2026 04:53:21 +0000 Subject: [PATCH 36/38] Bump urllib3 from 2.6.3 to 2.7.0 Bumps [urllib3](https://github.com/urllib3/urllib3) from 2.6.3 to 2.7.0. - [Release notes](https://github.com/urllib3/urllib3/releases) - [Changelog](https://github.com/urllib3/urllib3/blob/main/CHANGES.rst) - [Commits](https://github.com/urllib3/urllib3/compare/2.6.3...2.7.0) --- updated-dependencies: - dependency-name: urllib3 dependency-version: 2.7.0 dependency-type: direct:production update-type: version-update:semver-minor ... Signed-off-by: dependabot[bot] --- poetry.lock | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/poetry.lock b/poetry.lock index 9f18d54b..e41223ad 100644 --- a/poetry.lock +++ b/poetry.lock @@ -1443,14 +1443,14 @@ files = [ [[package]] name = "urllib3" -version = "2.6.3" +version = "2.7.0" description = "HTTP library with thread-safe connection pooling, file post, and more." optional = false -python-versions = ">=3.9" +python-versions = ">=3.10" groups = ["main"] files = [ - {file = "urllib3-2.6.3-py3-none-any.whl", hash = "sha256:bf272323e553dfb2e87d9bfd225ca7b0f467b919d7bbd355436d3fd37cb0acd4"}, - {file = "urllib3-2.6.3.tar.gz", hash = "sha256:1b62b6884944a57dbe321509ab94fd4d3b307075e0c2eae991ac71ee15ad38ed"}, + {file = "urllib3-2.7.0-py3-none-any.whl", hash = "sha256:9fb4c81ebbb1ce9531cce37674bbc6f1360472bc18ca9a553ede278ef7276897"}, + {file = "urllib3-2.7.0.tar.gz", hash = "sha256:231e0ec3b63ceb14667c67be60f2f2c40a518cb38b03af60abc813da26505f4c"}, ] [package.extras] From b0da2db1a3ce243f2c61111e53cbcdd670319622 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Fri, 15 May 2026 12:14:01 +0000 Subject: [PATCH 37/38] Bump requests from 2.32.5 to 2.34.2 Bumps [requests](https://github.com/psf/requests) from 2.32.5 to 2.34.2. - [Release notes](https://github.com/psf/requests/releases) - [Changelog](https://github.com/psf/requests/blob/main/HISTORY.md) - [Commits](https://github.com/psf/requests/compare/v2.32.5...v2.34.2) --- updated-dependencies: - dependency-name: requests dependency-version: 2.34.1 dependency-type: direct:production update-type: version-update:semver-minor ... Signed-off-by: dependabot[bot] --- poetry.lock | 14 +++++++------- 1 file changed, 7 insertions(+), 7 deletions(-) diff --git a/poetry.lock b/poetry.lock index e41223ad..858d10f6 100644 --- a/poetry.lock +++ b/poetry.lock @@ -1323,25 +1323,25 @@ files = [ [[package]] name = "requests" -version = "2.32.5" +version = "2.34.2" description = "Python HTTP for Humans." optional = false -python-versions = ">=3.9" +python-versions = ">=3.10" groups = ["main"] files = [ - {file = "requests-2.32.5-py3-none-any.whl", hash = "sha256:2462f94637a34fd532264295e186976db0f5d453d1cdd31473c85a6a161affb6"}, - {file = "requests-2.32.5.tar.gz", hash = "sha256:dbba0bac56e100853db0ea71b82b4dfd5fe2bf6d3754a8893c3af500cec7d7cf"}, + {file = "requests-2.34.2-py3-none-any.whl", hash = "sha256:2a0d60c172f83ac6ab31e4554906c0f3b3588d37b5cb939b1c061f4907e278e0"}, + {file = "requests-2.34.2.tar.gz", hash = "sha256:f288924cae4e29463698d6d60bc6a4da69c89185ad1e0bcc4104f584e960b9ed"}, ] [package.dependencies] -certifi = ">=2017.4.17" +certifi = ">=2023.5.7" charset_normalizer = ">=2,<4" idna = ">=2.5,<4" -urllib3 = ">=1.21.1,<3" +urllib3 = ">=1.26,<3" [package.extras] socks = ["PySocks (>=1.5.6,!=1.5.7)"] -use-chardet-on-py3 = ["chardet (>=3.0.2,<6)"] +use-chardet-on-py3 = ["chardet (>=3.0.2,<8)"] [[package]] name = "resolvelib" From 81a480e19c64cc1716c31c9bebf272702ffe86c2 Mon Sep 17 00:00:00 2001 From: Sathiyakumar Ganesan Date: Mon, 18 May 2026 10:35:39 +0100 Subject: [PATCH 38/38] Fix variable reference for source branch in setup-build-name.yml --- azure/templates/setup-build-name.yml | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/azure/templates/setup-build-name.yml b/azure/templates/setup-build-name.yml index 07258990..f0f466cb 100644 --- a/azure/templates/setup-build-name.yml +++ b/azure/templates/setup-build-name.yml @@ -5,14 +5,15 @@ parameters: steps: - bash: | - if [ -n "$(System.PullRequest.SourceBranch)" ]; then - export SOURCE_BRANCH="$(System.PullRequest.SourceBranch)" + if [ -n "$system_pull_request_source_branch" ]; then + export SOURCE_BRANCH="$system_pull_request_source_branch" else export SOURCE_BRANCH=`echo $(SERVICE_ARTIFACT_NAME) | cut -d'+' -f1 | sed -e "s/$service_name-//"` fi echo "##vso[task.setvariable variable=BRANCH_NAME]`echo $SOURCE_BRANCH | sed -r 's/[/|\\@":<>?*]+/-/g'`" env: service_name: ${{ parameters.service_name }} + system_pull_request_source_branch: $(System.PullRequest.SourceBranch) displayName: Set and replace invalid characters in branch name - bash: 'echo "##vso[build.updatebuildnumber]$service_name-$(BRANCH_NAME)+$(Build.BuildID)"'