From 66d9a7f1d895b0415aa5f112913bbd645b2b7972 Mon Sep 17 00:00:00 2001 From: Code and Solder Date: Thu, 10 Sep 2026 09:48:54 +0200 Subject: [PATCH 1/9] ci: apply blocked dependency maintenance --- .../workflows/maintenance-unblock-apply.yml | 153 ++++++++++++++++++ 1 file changed, 153 insertions(+) create mode 100644 .github/workflows/maintenance-unblock-apply.yml diff --git a/.github/workflows/maintenance-unblock-apply.yml b/.github/workflows/maintenance-unblock-apply.yml new file mode 100644 index 0000000..6316a97 --- /dev/null +++ b/.github/workflows/maintenance-unblock-apply.yml @@ -0,0 +1,153 @@ +name: Apply maintenance unblock + +on: + push: + branches: + - automation/maintenance-unblock-2026-09-10-z + +permissions: + contents: write + +jobs: + apply: + runs-on: ubuntu-24.04 + timeout-minutes: 30 + steps: + - uses: actions/checkout@v7 + with: + ref: automation/maintenance-unblock-2026-09-10-z + + - name: Patch dependency wrappers out + shell: bash + run: | + set -euo pipefail + python3 <<'PY' + from pathlib import Path + + def replace(path, old, new): + p = Path(path) + text = p.read_text() + if old not in text: + raise SystemExit(f"expected snippet not found in {path}: {old!r}") + p.write_text(text.replace(old, new)) + + replace( + "Cargo.toml", + 'axum-valid = "0.25"\nvalidator = { version = "0.20", features = ["derive"] }', + 'validator = { version = "0.21", features = ["derive"] }', + ) + replace( + "Cargo.toml", + 'testcontainers = "0.27"\ntestcontainers-modules = { version = "0.15", features = ["postgres"] }', + 'testcontainers = "0.28"', + ) + replace( + "gateway/Cargo.toml", + 'axum-valid = { workspace = true }\n', + '', + ) + replace( + "e2e-tests/Cargo.toml", + 'testcontainers-modules = { workspace = true }\n', + '', + ) + + replace( + "gateway/src/auth/handlers.rs", + '* Public auth request payloads are typed and validated with `axum-valid`.', + '* Public auth request payloads are typed and validated with `validator`.', + ) + replace( + "gateway/src/auth/handlers.rs", + '''pub async fn login_handler( + State(state): State, + session: tower_sessions::Session, + axum_valid::Valid(axum::Json(LoginRequest { user_id, password })): axum_valid::Valid< + axum::Json, + >, +) -> Result, AppError> { + let s = &state.settings;''', + '''pub async fn login_handler( + State(state): State, + session: tower_sessions::Session, + axum::Json(request): axum::Json, +) -> Result, AppError> { + request + .validate() + .map_err(|_| AppError::BadRequest("invalid request payload".to_string()))?; + let LoginRequest { user_id, password } = request; + let s = &state.settings;''', + ) + replace( + "gateway/src/auth/handlers.rs", + '''pub async fn refresh_handler( + State(state): State, + axum_valid::Valid(axum::Json(RefreshRequest { refresh_token })): axum_valid::Valid< + axum::Json, + >, +) -> Result, AppError> { + let pool = state.db_pool.as_ref().ok_or_else(|| {''', + '''pub async fn refresh_handler( + State(state): State, + axum::Json(request): axum::Json, +) -> Result, AppError> { + request + .validate() + .map_err(|_| AppError::BadRequest("invalid request payload".to_string()))?; + let RefreshRequest { refresh_token } = request; + let pool = state.db_pool.as_ref().ok_or_else(|| {''', + ) + + replace( + "e2e-tests/src/common/db.rs", + '''use testcontainers::runners::AsyncRunner; +use testcontainers::{ContainerAsync, ImageExt}; +use testcontainers_modules::postgres::Postgres;''', + '''use testcontainers::core::{IntoContainerPort, WaitFor}; +use testcontainers::runners::AsyncRunner; +use testcontainers::{ContainerAsync, GenericImage, ImageExt};''', + ) + replace( + "e2e-tests/src/common/db.rs", + 'container: Box>,', + 'container: Box>,', + ) + replace( + "e2e-tests/src/common/db.rs", + '''let container = Postgres::default() + .with_tag("17-alpine") + .start()''', + '''let container = GenericImage::new("postgres", "17-alpine") + .with_exposed_port(5432.tcp()) + .with_wait_for(WaitFor::message_on_stderr( + "database system is ready to accept connections", + )) + .with_wait_for(WaitFor::message_on_stdout( + "database system is ready to accept connections", + )) + .with_env_var("POSTGRES_DB", "postgres") + .with_env_var("POSTGRES_USER", "postgres") + .with_env_var("POSTGRES_PASSWORD", "postgres") + .start()''', + ) + PY + + - name: Update lockfile and validate + shell: bash + run: | + set -euo pipefail + rustup toolchain install 1.98.1 --profile minimal --component rustfmt + cargo +1.98.1 update + cargo +1.98.1 fmt --all --check + cargo +1.98.1 check --locked -p gateway-example -p e2e-tests --lib + + - name: Commit result and remove helper workflow + shell: bash + run: | + set -euo pipefail + git rm .github/workflows/maintenance-unblock-apply.yml + git config user.name github-actions[bot] + git config user.email 41898282+github-actions[bot]@users.noreply.github.com + git add Cargo.toml Cargo.lock gateway/Cargo.toml gateway/src/auth/handlers.rs e2e-tests/Cargo.toml e2e-tests/src/common/db.rs + git commit -m "chore: unblock queued dependency upgrades" + git push origin HEAD:automation/maintenance-unblock-2026-09-10-z From 6930f1b6859d98dfd783a206dbf1f9dc532684ce Mon Sep 17 00:00:00 2001 From: Code and Solder Date: Thu, 10 Sep 2026 09:50:41 +0200 Subject: [PATCH 2/9] ci: reduce maintenance helper to sanity check --- .../workflows/maintenance-unblock-apply.yml | 147 +----------------- 1 file changed, 3 insertions(+), 144 deletions(-) diff --git a/.github/workflows/maintenance-unblock-apply.yml b/.github/workflows/maintenance-unblock-apply.yml index 6316a97..16a6ccc 100644 --- a/.github/workflows/maintenance-unblock-apply.yml +++ b/.github/workflows/maintenance-unblock-apply.yml @@ -1,153 +1,12 @@ -name: Apply maintenance unblock +name: Maintenance branch sanity on: push: branches: - automation/maintenance-unblock-2026-09-10-z -permissions: - contents: write - jobs: - apply: + sanity: runs-on: ubuntu-24.04 - timeout-minutes: 30 steps: - - uses: actions/checkout@v7 - with: - ref: automation/maintenance-unblock-2026-09-10-z - - - name: Patch dependency wrappers out - shell: bash - run: | - set -euo pipefail - python3 <<'PY' - from pathlib import Path - - def replace(path, old, new): - p = Path(path) - text = p.read_text() - if old not in text: - raise SystemExit(f"expected snippet not found in {path}: {old!r}") - p.write_text(text.replace(old, new)) - - replace( - "Cargo.toml", - 'axum-valid = "0.25"\nvalidator = { version = "0.20", features = ["derive"] }', - 'validator = { version = "0.21", features = ["derive"] }', - ) - replace( - "Cargo.toml", - 'testcontainers = "0.27"\ntestcontainers-modules = { version = "0.15", features = ["postgres"] }', - 'testcontainers = "0.28"', - ) - replace( - "gateway/Cargo.toml", - 'axum-valid = { workspace = true }\n', - '', - ) - replace( - "e2e-tests/Cargo.toml", - 'testcontainers-modules = { workspace = true }\n', - '', - ) - - replace( - "gateway/src/auth/handlers.rs", - '* Public auth request payloads are typed and validated with `axum-valid`.', - '* Public auth request payloads are typed and validated with `validator`.', - ) - replace( - "gateway/src/auth/handlers.rs", - '''pub async fn login_handler( - State(state): State, - session: tower_sessions::Session, - axum_valid::Valid(axum::Json(LoginRequest { user_id, password })): axum_valid::Valid< - axum::Json, - >, -) -> Result, AppError> { - let s = &state.settings;''', - '''pub async fn login_handler( - State(state): State, - session: tower_sessions::Session, - axum::Json(request): axum::Json, -) -> Result, AppError> { - request - .validate() - .map_err(|_| AppError::BadRequest("invalid request payload".to_string()))?; - let LoginRequest { user_id, password } = request; - let s = &state.settings;''', - ) - replace( - "gateway/src/auth/handlers.rs", - '''pub async fn refresh_handler( - State(state): State, - axum_valid::Valid(axum::Json(RefreshRequest { refresh_token })): axum_valid::Valid< - axum::Json, - >, -) -> Result, AppError> { - let pool = state.db_pool.as_ref().ok_or_else(|| {''', - '''pub async fn refresh_handler( - State(state): State, - axum::Json(request): axum::Json, -) -> Result, AppError> { - request - .validate() - .map_err(|_| AppError::BadRequest("invalid request payload".to_string()))?; - let RefreshRequest { refresh_token } = request; - let pool = state.db_pool.as_ref().ok_or_else(|| {''', - ) - - replace( - "e2e-tests/src/common/db.rs", - '''use testcontainers::runners::AsyncRunner; -use testcontainers::{ContainerAsync, ImageExt}; -use testcontainers_modules::postgres::Postgres;''', - '''use testcontainers::core::{IntoContainerPort, WaitFor}; -use testcontainers::runners::AsyncRunner; -use testcontainers::{ContainerAsync, GenericImage, ImageExt};''', - ) - replace( - "e2e-tests/src/common/db.rs", - 'container: Box>,', - 'container: Box>,', - ) - replace( - "e2e-tests/src/common/db.rs", - '''let container = Postgres::default() - .with_tag("17-alpine") - .start()''', - '''let container = GenericImage::new("postgres", "17-alpine") - .with_exposed_port(5432.tcp()) - .with_wait_for(WaitFor::message_on_stderr( - "database system is ready to accept connections", - )) - .with_wait_for(WaitFor::message_on_stdout( - "database system is ready to accept connections", - )) - .with_env_var("POSTGRES_DB", "postgres") - .with_env_var("POSTGRES_USER", "postgres") - .with_env_var("POSTGRES_PASSWORD", "postgres") - .start()''', - ) - PY - - - name: Update lockfile and validate - shell: bash - run: | - set -euo pipefail - rustup toolchain install 1.98.1 --profile minimal --component rustfmt - cargo +1.98.1 update - cargo +1.98.1 fmt --all --check - cargo +1.98.1 check --locked -p gateway-example -p e2e-tests --lib - - - name: Commit result and remove helper workflow - shell: bash - run: | - set -euo pipefail - git rm .github/workflows/maintenance-unblock-apply.yml - git config user.name github-actions[bot] - git config user.email 41898282+github-actions[bot]@users.noreply.github.com - git add Cargo.toml Cargo.lock gateway/Cargo.toml gateway/src/auth/handlers.rs e2e-tests/Cargo.toml e2e-tests/src/common/db.rs - git commit -m "chore: unblock queued dependency upgrades" - git push origin HEAD:automation/maintenance-unblock-2026-09-10-z + - run: echo ok From d06a4ded293129aae5ea7f58a39331db380d02cd Mon Sep 17 00:00:00 2001 From: Code and Solder Date: Thu, 10 Sep 2026 09:51:32 +0200 Subject: [PATCH 3/9] ci: add maintenance patch helper --- .github/maintenance-unblock.py | 104 +++++++++++++++++++++++++++++++++ 1 file changed, 104 insertions(+) create mode 100644 .github/maintenance-unblock.py diff --git a/.github/maintenance-unblock.py b/.github/maintenance-unblock.py new file mode 100644 index 0000000..6dcce0b --- /dev/null +++ b/.github/maintenance-unblock.py @@ -0,0 +1,104 @@ +from pathlib import Path + + +def replace(path: str, old: str, new: str) -> None: + p = Path(path) + text = p.read_text() + if old not in text: + raise SystemExit(f"expected snippet not found in {path}: {old!r}") + p.write_text(text.replace(old, new)) + + +replace( + "Cargo.toml", + 'axum-valid = "0.25"\nvalidator = { version = "0.20", features = ["derive"] }', + 'validator = { version = "0.21", features = ["derive"] }', +) +replace( + "Cargo.toml", + 'testcontainers = "0.27"\ntestcontainers-modules = { version = "0.15", features = ["postgres"] }', + 'testcontainers = "0.28"', +) +replace("gateway/Cargo.toml", 'axum-valid = { workspace = true }\n', "") +replace("e2e-tests/Cargo.toml", 'testcontainers-modules = { workspace = true }\n', "") + +replace( + "gateway/src/auth/handlers.rs", + '* Public auth request payloads are typed and validated with `axum-valid`.', + '* Public auth request payloads are typed and validated with `validator`.', +) +replace( + "gateway/src/auth/handlers.rs", + '''pub async fn login_handler( + State(state): State, + session: tower_sessions::Session, + axum_valid::Valid(axum::Json(LoginRequest { user_id, password })): axum_valid::Valid< + axum::Json, + >, +) -> Result, AppError> { + let s = &state.settings;''', + '''pub async fn login_handler( + State(state): State, + session: tower_sessions::Session, + axum::Json(request): axum::Json, +) -> Result, AppError> { + request + .validate() + .map_err(|_| AppError::BadRequest("invalid request payload".to_string()))?; + let LoginRequest { user_id, password } = request; + let s = &state.settings;''', +) +replace( + "gateway/src/auth/handlers.rs", + '''pub async fn refresh_handler( + State(state): State, + axum_valid::Valid(axum::Json(RefreshRequest { refresh_token })): axum_valid::Valid< + axum::Json, + >, +) -> Result, AppError> { + let pool = state.db_pool.as_ref().ok_or_else(|| {''', + '''pub async fn refresh_handler( + State(state): State, + axum::Json(request): axum::Json, +) -> Result, AppError> { + request + .validate() + .map_err(|_| AppError::BadRequest("invalid request payload".to_string()))?; + let RefreshRequest { refresh_token } = request; + let pool = state.db_pool.as_ref().ok_or_else(|| {''', +) + +replace( + "e2e-tests/src/common/db.rs", + '''use testcontainers::runners::AsyncRunner; +use testcontainers::{ContainerAsync, ImageExt}; +use testcontainers_modules::postgres::Postgres;''', + '''use testcontainers::core::{IntoContainerPort, WaitFor}; +use testcontainers::runners::AsyncRunner; +use testcontainers::{ContainerAsync, GenericImage, ImageExt};''', +) +replace( + "e2e-tests/src/common/db.rs", + "container: Box>, +", + "container: Box>, +", +) +replace( + "e2e-tests/src/common/db.rs", + '''let container = Postgres::default() + .with_tag("17-alpine") + .start()''', + '''let container = GenericImage::new("postgres", "17-alpine") + .with_exposed_port(5432.tcp()) + .with_wait_for(WaitFor::message_on_stderr( + "database system is ready to accept connections", + )) + .with_wait_for(WaitFor::message_on_stdout( + "database system is ready to accept connections", + )) + .with_env_var("POSTGRES_DB", "postgres") + .with_env_var("POSTGRES_USER", "postgres") + .with_env_var("POSTGRES_PASSWORD", "postgres") + .start()''', +) From e6ecd2af57e09e4e8ff055eee59aca5d4ec87272 Mon Sep 17 00:00:00 2001 From: Code and Solder Date: Thu, 10 Sep 2026 09:51:57 +0200 Subject: [PATCH 4/9] ci: run maintenance patch and lockfile refresh --- .../workflows/maintenance-unblock-apply.yml | 31 +++++++++++++++++-- 1 file changed, 28 insertions(+), 3 deletions(-) diff --git a/.github/workflows/maintenance-unblock-apply.yml b/.github/workflows/maintenance-unblock-apply.yml index 16a6ccc..e325fa2 100644 --- a/.github/workflows/maintenance-unblock-apply.yml +++ b/.github/workflows/maintenance-unblock-apply.yml @@ -1,12 +1,37 @@ -name: Maintenance branch sanity +name: Apply maintenance unblock on: push: branches: - automation/maintenance-unblock-2026-09-10-z +permissions: + contents: write + jobs: - sanity: + apply: runs-on: ubuntu-24.04 + timeout-minutes: 30 steps: - - run: echo ok + - uses: actions/checkout@v7 + with: + ref: automation/maintenance-unblock-2026-09-10-z + + - name: Apply source changes + run: python3 .github/maintenance-unblock.py + + - name: Refresh lockfile and check changed crates + run: | + rustup toolchain install 1.98.1 --profile minimal --component rustfmt + cargo +1.98.1 update + cargo +1.98.1 fmt --all + cargo +1.98.1 check --locked -p gateway-example -p e2e-tests --lib + + - name: Commit maintenance result + run: | + git rm .github/maintenance-unblock.py .github/workflows/maintenance-unblock-apply.yml + git config user.name github-actions[bot] + git config user.email 41898282+github-actions[bot]@users.noreply.github.com + git add Cargo.toml Cargo.lock gateway/Cargo.toml gateway/src/auth/handlers.rs e2e-tests/Cargo.toml e2e-tests/src/common/db.rs + git commit -m "chore: unblock queued dependency upgrades" + git push origin HEAD:automation/maintenance-unblock-2026-09-10-z From 4f6fbbfadeecd8775f88dcf6385a852f06b1208e Mon Sep 17 00:00:00 2001 From: Code and Solder Date: Thu, 10 Sep 2026 09:52:41 +0200 Subject: [PATCH 5/9] ci: fix maintenance patch helper quoting --- .github/maintenance-unblock.py | 7 +++---- 1 file changed, 3 insertions(+), 4 deletions(-) diff --git a/.github/maintenance-unblock.py b/.github/maintenance-unblock.py index 6dcce0b..fa8b903 100644 --- a/.github/maintenance-unblock.py +++ b/.github/maintenance-unblock.py @@ -79,10 +79,8 @@ def replace(path: str, old: str, new: str) -> None: ) replace( "e2e-tests/src/common/db.rs", - "container: Box>, -", - "container: Box>, -", + '''container: Box>,''', + '''container: Box>,''', ) replace( "e2e-tests/src/common/db.rs", @@ -100,5 +98,6 @@ def replace(path: str, old: str, new: str) -> None: .with_env_var("POSTGRES_DB", "postgres") .with_env_var("POSTGRES_USER", "postgres") .with_env_var("POSTGRES_PASSWORD", "postgres") + .with_cmd(["-c", "fsync=off"]) .start()''', ) From 33f029c1dd5d145fd6b40fd52ee562f343488f80 Mon Sep 17 00:00:00 2001 From: "github-actions[bot]" <41898282+github-actions[bot]@users.noreply.github.com> Date: Thu, 10 Sep 2026 07:59:31 +0000 Subject: [PATCH 6/9] chore: unblock queued dependency upgrades --- .github/maintenance-unblock.py | 103 ------------------ .../workflows/maintenance-unblock-apply.yml | 37 ------- Cargo.lock | 57 +++------- Cargo.toml | 6 +- e2e-tests/Cargo.toml | 1 - e2e-tests/src/common/db.rs | 20 +++- gateway/Cargo.toml | 1 - gateway/src/auth/handlers.rs | 18 +-- 8 files changed, 45 insertions(+), 198 deletions(-) delete mode 100644 .github/maintenance-unblock.py delete mode 100644 .github/workflows/maintenance-unblock-apply.yml diff --git a/.github/maintenance-unblock.py b/.github/maintenance-unblock.py deleted file mode 100644 index fa8b903..0000000 --- a/.github/maintenance-unblock.py +++ /dev/null @@ -1,103 +0,0 @@ -from pathlib import Path - - -def replace(path: str, old: str, new: str) -> None: - p = Path(path) - text = p.read_text() - if old not in text: - raise SystemExit(f"expected snippet not found in {path}: {old!r}") - p.write_text(text.replace(old, new)) - - -replace( - "Cargo.toml", - 'axum-valid = "0.25"\nvalidator = { version = "0.20", features = ["derive"] }', - 'validator = { version = "0.21", features = ["derive"] }', -) -replace( - "Cargo.toml", - 'testcontainers = "0.27"\ntestcontainers-modules = { version = "0.15", features = ["postgres"] }', - 'testcontainers = "0.28"', -) -replace("gateway/Cargo.toml", 'axum-valid = { workspace = true }\n', "") -replace("e2e-tests/Cargo.toml", 'testcontainers-modules = { workspace = true }\n', "") - -replace( - "gateway/src/auth/handlers.rs", - '* Public auth request payloads are typed and validated with `axum-valid`.', - '* Public auth request payloads are typed and validated with `validator`.', -) -replace( - "gateway/src/auth/handlers.rs", - '''pub async fn login_handler( - State(state): State, - session: tower_sessions::Session, - axum_valid::Valid(axum::Json(LoginRequest { user_id, password })): axum_valid::Valid< - axum::Json, - >, -) -> Result, AppError> { - let s = &state.settings;''', - '''pub async fn login_handler( - State(state): State, - session: tower_sessions::Session, - axum::Json(request): axum::Json, -) -> Result, AppError> { - request - .validate() - .map_err(|_| AppError::BadRequest("invalid request payload".to_string()))?; - let LoginRequest { user_id, password } = request; - let s = &state.settings;''', -) -replace( - "gateway/src/auth/handlers.rs", - '''pub async fn refresh_handler( - State(state): State, - axum_valid::Valid(axum::Json(RefreshRequest { refresh_token })): axum_valid::Valid< - axum::Json, - >, -) -> Result, AppError> { - let pool = state.db_pool.as_ref().ok_or_else(|| {''', - '''pub async fn refresh_handler( - State(state): State, - axum::Json(request): axum::Json, -) -> Result, AppError> { - request - .validate() - .map_err(|_| AppError::BadRequest("invalid request payload".to_string()))?; - let RefreshRequest { refresh_token } = request; - let pool = state.db_pool.as_ref().ok_or_else(|| {''', -) - -replace( - "e2e-tests/src/common/db.rs", - '''use testcontainers::runners::AsyncRunner; -use testcontainers::{ContainerAsync, ImageExt}; -use testcontainers_modules::postgres::Postgres;''', - '''use testcontainers::core::{IntoContainerPort, WaitFor}; -use testcontainers::runners::AsyncRunner; -use testcontainers::{ContainerAsync, GenericImage, ImageExt};''', -) -replace( - "e2e-tests/src/common/db.rs", - '''container: Box>,''', - '''container: Box>,''', -) -replace( - "e2e-tests/src/common/db.rs", - '''let container = Postgres::default() - .with_tag("17-alpine") - .start()''', - '''let container = GenericImage::new("postgres", "17-alpine") - .with_exposed_port(5432.tcp()) - .with_wait_for(WaitFor::message_on_stderr( - "database system is ready to accept connections", - )) - .with_wait_for(WaitFor::message_on_stdout( - "database system is ready to accept connections", - )) - .with_env_var("POSTGRES_DB", "postgres") - .with_env_var("POSTGRES_USER", "postgres") - .with_env_var("POSTGRES_PASSWORD", "postgres") - .with_cmd(["-c", "fsync=off"]) - .start()''', -) diff --git a/.github/workflows/maintenance-unblock-apply.yml b/.github/workflows/maintenance-unblock-apply.yml deleted file mode 100644 index e325fa2..0000000 --- a/.github/workflows/maintenance-unblock-apply.yml +++ /dev/null @@ -1,37 +0,0 @@ -name: Apply maintenance unblock - -on: - push: - branches: - - automation/maintenance-unblock-2026-09-10-z - -permissions: - contents: write - -jobs: - apply: - runs-on: ubuntu-24.04 - timeout-minutes: 30 - steps: - - uses: actions/checkout@v7 - with: - ref: automation/maintenance-unblock-2026-09-10-z - - - name: Apply source changes - run: python3 .github/maintenance-unblock.py - - - name: Refresh lockfile and check changed crates - run: | - rustup toolchain install 1.98.1 --profile minimal --component rustfmt - cargo +1.98.1 update - cargo +1.98.1 fmt --all - cargo +1.98.1 check --locked -p gateway-example -p e2e-tests --lib - - - name: Commit maintenance result - run: | - git rm .github/maintenance-unblock.py .github/workflows/maintenance-unblock-apply.yml - git config user.name github-actions[bot] - git config user.email 41898282+github-actions[bot]@users.noreply.github.com - git add Cargo.toml Cargo.lock gateway/Cargo.toml gateway/src/auth/handlers.rs e2e-tests/Cargo.toml e2e-tests/src/common/db.rs - git commit -m "chore: unblock queued dependency upgrades" - git push origin HEAD:automation/maintenance-unblock-2026-09-10-z diff --git a/Cargo.lock b/Cargo.lock index 1a091c0..3abea57 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -346,16 +346,6 @@ dependencies = [ "tracing-opentelemetry-instrumentation-sdk", ] -[[package]] -name = "axum-valid" -version = "0.25.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "6c0cba26dfbdeff786711f747a51efd368c3864ab9574981ae532514dd492dd7" -dependencies = [ - "axum", - "validator", -] - [[package]] name = "base16" version = "0.2.1" @@ -424,9 +414,9 @@ dependencies = [ [[package]] name = "bollard" -version = "0.20.2" +version = "0.21.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ee04c4c84f1f811b017f2fbb7dd8815c976e7ca98593de9c1e2afad0f636bff4" +checksum = "dbe8358268799ebb3e4df23cb9d47f4c72bbc4f5247e2fa6a1bf7b6c0baea220" dependencies = [ "async-stream", "base64 0.22.1", @@ -448,7 +438,7 @@ dependencies = [ "log", "num", "pin-project-lite", - "rand 0.9.5", + "rand 0.10.2", "rustls", "rustls-native-certs", "rustls-pki-types", @@ -469,22 +459,21 @@ dependencies = [ [[package]] name = "bollard-buildkit-proto" -version = "0.7.0" +version = "0.8.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "85a885520bf6249ab931a764ffdb87b0ceef48e6e7d807cfdb21b751e086e1ad" +checksum = "b5c97450e79c7c565302dd92e86b08823b47550fcb4fc5ce910194d1b087a1a3" dependencies = [ "prost", "prost-types", "tonic", "tonic-prost", - "ureq", ] [[package]] name = "bollard-stubs" -version = "1.52.1-rc.29.1.3" +version = "1.53.1-rc.29.3.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "0f0a8ca8799131c1837d1282c3f81f31e76ceb0ce426e04a7fe1ccee3287c066" +checksum = "ce412eb6f7096743011dc3cb5c674caeb24ced61d8c498fe07cf7998a4fea889" dependencies = [ "base64 0.22.1", "bollard-buildkit-proto", @@ -780,9 +769,9 @@ dependencies = [ [[package]] name = "console" -version = "0.16.4" +version = "0.16.6" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "4fe5f465a4f6fee88fad41b85d990f84c835335e85b5d9e6e63e0d06d28cba7c" +checksum = "e96a4956774c13c126a8b5af4daa79384f4d826534c95a02d76afb39e2ab64e3" dependencies = [ "encode_unicode", "libc", @@ -1419,7 +1408,6 @@ dependencies = [ "serde_json", "sqlx", "testcontainers", - "testcontainers-modules", "tokio", "tokio-util", "tower-http 0.7.1", @@ -1777,7 +1765,6 @@ dependencies = [ "axum-prometheus", "axum-tower-sessions-csrf", "axum-tracing-opentelemetry", - "axum-valid", "base64 0.23.1", "chrono", "console-subscriber", @@ -4203,9 +4190,9 @@ dependencies = [ [[package]] name = "parse-display" -version = "0.9.1" +version = "0.11.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "914a1c2265c98e2446911282c6ac86d8524f495792c38c5bd884f80499c7538a" +checksum = "e78deb158fb1d73b29efb4b7e9b9860b78059c670de06bd28df8d0b458ded0eb" dependencies = [ "parse-display-derive", "regex", @@ -4214,9 +4201,9 @@ dependencies = [ [[package]] name = "parse-display-derive" -version = "0.9.1" +version = "0.11.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "2ae7800a4c974efd12df917266338e79a7a74415173caf7e70aa0a0707345281" +checksum = "8e95a50d1084dab562913062c4c34bb204b68fc6ec38a1395909ff5aaaf4f10a" dependencies = [ "proc-macro2", "quote", @@ -6073,9 +6060,9 @@ dependencies = [ [[package]] name = "testcontainers" -version = "0.27.3" +version = "0.28.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "bfd5785b5483672915ed5fe3cddf9f546802779fc1eceff0a6fb7321fac81c1e" +checksum = "6e2bbe381afaaa58ea610c5fc3ffb2184063a32b3e358a179f0b4865dd59934a" dependencies = [ "astral-tokio-tar", "async-trait", @@ -6102,15 +6089,6 @@ dependencies = [ "url", ] -[[package]] -name = "testcontainers-modules" -version = "0.15.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e5985fde5befe4ffa77a052e035e16c2da86e8bae301baa9f9904ad3c494d357" -dependencies = [ - "testcontainers", -] - [[package]] name = "thiserror" version = "1.0.69" @@ -7003,12 +6981,11 @@ dependencies = [ [[package]] name = "validator" -version = "0.20.0" +version = "0.21.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "43fb22e1a008ece370ce08a3e9e4447a910e92621bb49b85d6e48a45397e7cfa" +checksum = "c3d68c6633c483df6780cc5277a417c7c2d1bceee2649d06c8ab6b0fd2dd3c81" dependencies = [ "idna", - "once_cell", "regex", "serde", "serde_derive", diff --git a/Cargo.toml b/Cargo.toml index ae2d32f..c2adfcd 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -43,8 +43,7 @@ uuid = { version = "1.26", features = ["serde"] } wasm-bindgen = "0.2" axum-prometheus = "0.10" tower_governor = { version = "0.8", features = ["axum"] } -axum-valid = "0.25" -validator = { version = "0.20", features = ["derive"] } +validator = { version = "0.21", features = ["derive"] } aws-lc-rs = "1" moka = { version = "0.12", features = ["future"] } stylance = "0.8" @@ -68,8 +67,7 @@ console-subscriber = "0.5" criterion = { version = "0.8", features = ["html_reports"] } insta = "1" proptest = "1" -testcontainers = "0.27" -testcontainers-modules = { version = "0.15", features = ["postgres"] } +testcontainers = "0.28" # Layered config: TOML file + RWF_* environment variable overrides. # The `config` crate gives us typed deserialization + per-source priority # without needing unsafe or hand-rolled merge logic. diff --git a/e2e-tests/Cargo.toml b/e2e-tests/Cargo.toml index 54d4dc8..158fb6f 100644 --- a/e2e-tests/Cargo.toml +++ b/e2e-tests/Cargo.toml @@ -77,7 +77,6 @@ opentelemetry_sdk = { workspace = true, optional = true, features = ["testing"] # Test infrastructure testcontainers = { workspace = true } -testcontainers-modules = { workspace = true } # Snapshot testing & property-based testing insta = { workspace = true, features = ["json"] } diff --git a/e2e-tests/src/common/db.rs b/e2e-tests/src/common/db.rs index b2a8113..b4187cf 100644 --- a/e2e-tests/src/common/db.rs +++ b/e2e-tests/src/common/db.rs @@ -6,16 +6,16 @@ use anyhow::{Context, Result}; use sqlx::PgPool; +use testcontainers::core::{IntoContainerPort, WaitFor}; use testcontainers::runners::AsyncRunner; -use testcontainers::{ContainerAsync, ImageExt}; -use testcontainers_modules::postgres::Postgres; +use testcontainers::{ContainerAsync, GenericImage, ImageExt}; /// RAII guard for a test-scoped Postgres database. pub struct TestEnv { pool: PgPool, connection_string: String, #[allow(dead_code, reason = "Kept alive for Drop side-effect on TestEnv")] - container: Box>, + container: Box>, } impl std::fmt::Debug for TestEnv { @@ -40,8 +40,18 @@ impl TestEnv { /// Returns an error if the container cannot start, its host/port cannot be /// resolved, the pool cannot connect, migrations fail, or seeding fails. pub async fn postgres() -> Result { - let container = Postgres::default() - .with_tag("17-alpine") + let container = GenericImage::new("postgres", "17-alpine") + .with_exposed_port(5432.tcp()) + .with_wait_for(WaitFor::message_on_stderr( + "database system is ready to accept connections", + )) + .with_wait_for(WaitFor::message_on_stdout( + "database system is ready to accept connections", + )) + .with_env_var("POSTGRES_DB", "postgres") + .with_env_var("POSTGRES_USER", "postgres") + .with_env_var("POSTGRES_PASSWORD", "postgres") + .with_cmd(["-c", "fsync=off"]) .start() .await .context("Failed to start Postgres testcontainer")?; diff --git a/gateway/Cargo.toml b/gateway/Cargo.toml index a974d31..93929af 100644 --- a/gateway/Cargo.toml +++ b/gateway/Cargo.toml @@ -21,7 +21,6 @@ dev-tools = ["dep:console-subscriber"] anyhow = { workspace = true } axum = { workspace = true } axum-prometheus = { workspace = true } -axum-valid = { workspace = true } chrono = { workspace = true } aws-lc-rs = { workspace = true } base64 = { workspace = true } diff --git a/gateway/src/auth/handlers.rs b/gateway/src/auth/handlers.rs index 258e070..3d72464 100644 --- a/gateway/src/auth/handlers.rs +++ b/gateway/src/auth/handlers.rs @@ -7,7 +7,7 @@ //! * Rate limiting is applied at the router level via `tower_governor`. //! * JWT errors are mapped to distinct error variants. //! * Secrets (passwords, tokens) are never included in log output. -//! * Public auth request payloads are typed and validated with `axum-valid`. +//! * Public auth request payloads are typed and validated with `validator`. use std::str::FromStr; @@ -88,10 +88,12 @@ pub struct ProtectedResponse { pub async fn login_handler( State(state): State, session: tower_sessions::Session, - axum_valid::Valid(axum::Json(LoginRequest { user_id, password })): axum_valid::Valid< - axum::Json, - >, + axum::Json(request): axum::Json, ) -> Result, AppError> { + request + .validate() + .map_err(|_| AppError::BadRequest("invalid request payload".to_string()))?; + let LoginRequest { user_id, password } = request; let s = &state.settings; let parsed_user_id = UserId::from_str(&user_id)?; @@ -219,10 +221,12 @@ async fn remove_unissued_refresh_token(pool: &sqlx::PgPool, refresh_jti: Uuid) { /// access, or JWT creation fails. pub async fn refresh_handler( State(state): State, - axum_valid::Valid(axum::Json(RefreshRequest { refresh_token })): axum_valid::Valid< - axum::Json, - >, + axum::Json(request): axum::Json, ) -> Result, AppError> { + request + .validate() + .map_err(|_| AppError::BadRequest("invalid request payload".to_string()))?; + let RefreshRequest { refresh_token } = request; let pool = state.db_pool.as_ref().ok_or_else(|| { AppError::internal( "refresh-token store unavailable", From a0fb20e43cf19a4ccd71d67128db2f54be7e4006 Mon Sep 17 00:00:00 2001 From: Code and Solder Date: Thu, 10 Sep 2026 10:00:34 +0200 Subject: [PATCH 7/9] ci: stage maintenance body quoting fix --- .github/fix-maintenance-markdown.py | 13 +++++++++++++ 1 file changed, 13 insertions(+) create mode 100644 .github/fix-maintenance-markdown.py diff --git a/.github/fix-maintenance-markdown.py b/.github/fix-maintenance-markdown.py new file mode 100644 index 0000000..ddda007 --- /dev/null +++ b/.github/fix-maintenance-markdown.py @@ -0,0 +1,13 @@ +from pathlib import Path + +path = Path('.github/workflows/maintenance-queue.yml') +text = path.read_text() +for token in ( + '`cargo upgrade`', + '`cargo update`', + '`Cargo.toml`', + '`Cargo.lock`', + '`SKILL.md`', +): + text = text.replace(token, token.replace('`', r'\`')) +path.write_text(text) From fb39b1ff11796780a8d8573f9e7001ffccbc2414 Mon Sep 17 00:00:00 2001 From: Code and Solder Date: Thu, 10 Sep 2026 10:00:41 +0200 Subject: [PATCH 8/9] ci: apply maintenance Markdown fix --- .../workflows/fix-maintenance-markdown.yml | 26 +++++++++++++++++++ 1 file changed, 26 insertions(+) create mode 100644 .github/workflows/fix-maintenance-markdown.yml diff --git a/.github/workflows/fix-maintenance-markdown.yml b/.github/workflows/fix-maintenance-markdown.yml new file mode 100644 index 0000000..7eb187e --- /dev/null +++ b/.github/workflows/fix-maintenance-markdown.yml @@ -0,0 +1,26 @@ +name: Fix maintenance Markdown + +on: + push: + branches: + - automation/maintenance-unblock-2026-09-10-z + +permissions: + contents: write + +jobs: + apply: + runs-on: ubuntu-24.04 + steps: + - uses: actions/checkout@v7 + with: + ref: automation/maintenance-unblock-2026-09-10-z + - run: python3 .github/fix-maintenance-markdown.py + - name: Commit fix + run: | + git rm .github/fix-maintenance-markdown.py .github/workflows/fix-maintenance-markdown.yml + git config user.name github-actions[bot] + git config user.email 41898282+github-actions[bot]@users.noreply.github.com + git add .github/workflows/maintenance-queue.yml + git commit -m "fix: preserve Markdown in maintenance issue bodies" + git push origin HEAD:automation/maintenance-unblock-2026-09-10-z From 8ccedea1098f8e868cd0de7a34e3d8526df3e3c0 Mon Sep 17 00:00:00 2001 From: "github-actions[bot]" <41898282+github-actions[bot]@users.noreply.github.com> Date: Thu, 10 Sep 2026 08:00:49 +0000 Subject: [PATCH 9/9] fix: preserve Markdown in maintenance issue bodies --- .github/fix-maintenance-markdown.py | 13 ---------- .../workflows/fix-maintenance-markdown.yml | 26 ------------------- .github/workflows/maintenance-queue.yml | 14 +++++----- 3 files changed, 7 insertions(+), 46 deletions(-) delete mode 100644 .github/fix-maintenance-markdown.py delete mode 100644 .github/workflows/fix-maintenance-markdown.yml diff --git a/.github/fix-maintenance-markdown.py b/.github/fix-maintenance-markdown.py deleted file mode 100644 index ddda007..0000000 --- a/.github/fix-maintenance-markdown.py +++ /dev/null @@ -1,13 +0,0 @@ -from pathlib import Path - -path = Path('.github/workflows/maintenance-queue.yml') -text = path.read_text() -for token in ( - '`cargo upgrade`', - '`cargo update`', - '`Cargo.toml`', - '`Cargo.lock`', - '`SKILL.md`', -): - text = text.replace(token, token.replace('`', r'\`')) -path.write_text(text) diff --git a/.github/workflows/fix-maintenance-markdown.yml b/.github/workflows/fix-maintenance-markdown.yml deleted file mode 100644 index 7eb187e..0000000 --- a/.github/workflows/fix-maintenance-markdown.yml +++ /dev/null @@ -1,26 +0,0 @@ -name: Fix maintenance Markdown - -on: - push: - branches: - - automation/maintenance-unblock-2026-09-10-z - -permissions: - contents: write - -jobs: - apply: - runs-on: ubuntu-24.04 - steps: - - uses: actions/checkout@v7 - with: - ref: automation/maintenance-unblock-2026-09-10-z - - run: python3 .github/fix-maintenance-markdown.py - - name: Commit fix - run: | - git rm .github/fix-maintenance-markdown.py .github/workflows/fix-maintenance-markdown.yml - git config user.name github-actions[bot] - git config user.email 41898282+github-actions[bot]@users.noreply.github.com - git add .github/workflows/maintenance-queue.yml - git commit -m "fix: preserve Markdown in maintenance issue bodies" - git push origin HEAD:automation/maintenance-unblock-2026-09-10-z diff --git a/.github/workflows/maintenance-queue.yml b/.github/workflows/maintenance-queue.yml index 2727599..7211df1 100644 --- a/.github/workflows/maintenance-queue.yml +++ b/.github/workflows/maintenance-queue.yml @@ -135,11 +135,11 @@ jobs: - `cargo upgrade` reports a newer manifest dependency requirement for **${dep}**. + \`cargo upgrade\` reports a newer manifest dependency requirement for **${dep}**. ${details} - Update this dependency everywhere appropriate, including incompatible/major changes. Read the crate's changelog/release notes where useful, adapt code to current APIs and best practices, refresh `Cargo.lock`, and run the full relevant CI/test suite. Do not skip an update merely because it is transitive, low-risk, or already SemVer-compatible. + Update this dependency everywhere appropriate, including incompatible/major changes. Read the crate's changelog/release notes where useful, adapt code to current APIs and best practices, refresh \`Cargo.lock\`, and run the full relevant CI/test suite. Do not skip an update merely because it is transitive, low-risk, or already SemVer-compatible. This issue is generated and refreshed automatically. A ChatGPT maintenance automation is expected to implement it. EOF @@ -179,7 +179,7 @@ jobs: log="$(tail -n 200 /tmp/cargo-upgrade.log)" body="$(cat < - The passive dependency detector failed while running `cargo upgrade`. + The passive dependency detector failed while running \`cargo upgrade\`. \`\`\`text ${log} @@ -233,7 +233,7 @@ jobs: - A plain `cargo update` changes `Cargo.lock`, so there are compatible and/or transitive dependency updates available even if no manifest requirement needs changing. + A plain \`cargo update\` changes \`Cargo.lock\`, so there are compatible and/or transitive dependency updates available even if no manifest requirement needs changing. \`\`\`text ${stat} @@ -252,7 +252,7 @@ jobs: gh issue create --repo "$REPO" --title "deps: refresh Cargo.lock" --label "automation:lockfile" --body "$body" fi elif [[ -n "$existing" ]]; then - gh issue close "$existing" --repo "$REPO" --reason completed --comment "Closed automatically: `cargo update` no longer changes Cargo.lock." + gh issue close "$existing" --repo "$REPO" --reason completed --comment "Closed automatically: \`cargo update\` no longer changes Cargo.lock." fi - name: Report cargo-update detector failure @@ -268,7 +268,7 @@ jobs: log="$(tail -n 200 /tmp/cargo-update.log)" body="$(cat < - The passive dependency detector failed while running `cargo update`. + The passive dependency detector failed while running \`cargo update\`. \`\`\`text ${log} @@ -321,7 +321,7 @@ jobs: - Read the official Rust ${VERSION} announcement and detailed release notes/changelog. - Review relevant language, standard-library, rustc, Cargo, Clippy, rustfmt, target/platform, and tooling changes. - - Review all Rust code, `Cargo.toml`/`Cargo.lock`, CI, Docker/build tooling, `SKILL.md`, README/docs, examples, lints, and project conventions for newly preferable patterns or obsolete workarounds. + - Review all Rust code, \`Cargo.toml\`/\`Cargo.lock\`, CI, Docker/build tooling, \`SKILL.md\`, README/docs, examples, lints, and project conventions for newly preferable patterns or obsolete workarounds. - Adopt useful newly-stable features and current best practices where they improve the project; remove workarounds that the new stable release makes unnecessary. - Update the current-stable CI/toolchain references while preserving the intentional MSRV contract unless there is a separately justified reason to raise it. - Run the full CI-equivalent test matrix and fix fallout.