phase1: CI gating, real OpenAPI, debug logging, test fixes#5
phase1: CI gating, real OpenAPI, debug logging, test fixes#5dominicusin merged 27 commits intomainfrom
Conversation
…ases in roundToCurrency
…rrency: fix rounding with Rational
… bankers rounding edge cases
… rounding with Rational
…unding; avoid FP error
…ounding to reduce FP errors
…n_bounds for stable -m matching
WalkthroughImplements Phase‑1 stabilization: serves a real OpenAPI 3.0.3 spec at Changes
Sequence Diagram(s)sequenceDiagram
autonumber
participant Client as Client
participant Server as Servant API
participant Auth as AuthMiddleware
participant DB as Database
participant Logger as DebugLogger (env OPENPAPYRUS_DEBUG)
Client->>Server: GET /api/v1/health/ready or POST /api/v1/login
Server->>Auth: pass request
Auth->>Logger: debug path/method (if enabled)
alt health readiness
Auth->>DB: ping / health check
DB-->>Auth: status (ok / not ok)
Auth->>Logger: debug DB failure (if not ok)
Auth-->>Server: respond 200/401/503 accordingly
else login
Auth->>Server: validate credentials
Server->>Logger: debug login success/failure
Server-->>Client: auth response (token / error)
end
Estimated code review effort🎯 3 (Moderate) | ⏱️ ~25 minutes Possibly related PRs
Poem
🚥 Pre-merge checks | ✅ 3✅ Passed checks (3 passed)
✏️ Tip: You can configure your own custom pre-merge checks in the settings. ✨ Finishing Touches🧪 Generate unit tests (beta)
Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out. Comment |
Review Summary by QodoPhase 1: Real OpenAPI, RBAC test gating, debug logging, and currency rounding fixes
WalkthroughsDescription• Implement real OpenAPI 3.0.3 spec replacing placeholder string • Add environment-based RBAC test gating for CI stability • Introduce centralized debug logging with OPENPAPYRUS_DEBUG flag • Fix currency rounding using Rational to eliminate floating-point errors • Refactor CI workflow and add docker-compose integration • Add swagger.json to public paths and enhance test coverage Diagramflowchart LR
A["OpenAPI Spec<br/>src/Surypus/API/OpenApi.hs"] -->|replaces placeholder| B["Root.hs<br/>apiSwaggerSpec"]
B -->|serves at| C["/swagger.json<br/>public endpoint"]
D["Debug Logging<br/>Surypus.Logging"] -->|controls via| E["OPENPAPYRUS_DEBUG=1"]
E -->|used in| F["AuthMiddleware<br/>Server.hs"]
G["RBAC Test Gating<br/>OPENPAPYRUS_SKIP_RBAC_TESTS"] -->|gates| H["RBACSpec.hs<br/>at main level"]
I["Currency Rounding<br/>Rational-based"] -->|fixes| J["FP edge cases<br/>in roundToCurrency"]
K["CI Workflow<br/>.github/workflows/ci.yml"] -->|integrates| L["Docker Compose<br/>DB + API"]
File Changes1. src/Surypus/API/OpenApi.hs
|
Code Review by Qodo
|
| {-# LANGUAGE OverloadedStrings #-} | ||
|
|
||
| module Surypus.API.OpenApi (apiSwaggerSpec) where | ||
|
|
||
| import Data.Aeson (Value, object, (.=)) | ||
|
|
||
| apiSwaggerSpec :: Value | ||
| apiSwaggerSpec = |
There was a problem hiding this comment.
1. openapi lacks haddock docs 📘 Rule violation ⚙ Maintainability
The new exposed module Surypus.API.OpenApi does not include a Haddock module header and the exported apiSwaggerSpec has no Haddock comment. This reduces public API maintainability and violates the documentation requirement for public modules/functions.
Agent Prompt
## Issue description
`src/Surypus/API/OpenApi.hs` is an exposed/public module but lacks a Haddock module header and the exported `apiSwaggerSpec` is undocumented.
## Issue Context
The project requires Haddock documentation for public modules and public API functions.
## Fix Focus Areas
- src/Surypus/API/OpenApi.hs[1-8]
ⓘ Copy this prompt and use it to remediate the issue with your preferred AI generation tools
| if docker-compose exec -T db pg_isready -U surypus -d surypus >/dev/null 2>&1; then | ||
| echo "DB is ready" | tee -a "$BUILD_LOG" | ||
| break | ||
| fi | ||
| sleep 2 |
There was a problem hiding this comment.
2. Tabs in ci-runner.sh 📘 Rule violation ⚙ Maintainability
The new scripts/ci-runner.sh uses tab characters for indentation inside the DB readiness loop. This violates the formatting rule requiring 2-space indentation and no tabs.
Agent Prompt
## Issue description
The new Bash script uses tab indentation, which violates the project's formatting rules.
## Issue Context
Consistent whitespace (2 spaces, no tabs) is required to reduce diff noise and keep formatting consistent.
## Fix Focus Areas
- scripts/ci-runner.sh[15-19]
ⓘ Copy this prompt and use it to remediate the issue with your preferred AI generation tools
| [ "rcrName" .= ("admin-test" :: Text), | ||
| "rcrPermissions" .= (["goods:write"] :: [Text]), | ||
| "rcrResources" .= ([] :: [Maybe Text]) |
There was a problem hiding this comment.
3. Unpacked text literals in tests 📘 Rule violation ≡ Correctness
test/API/ServerSpec.hs introduces Text values via raw string literals (e.g., `("admin-test" ::
Text)), rather than packing with T.pack. This violates the test Text` literal convention and can
create inconsistent Text construction patterns.
Agent Prompt
## Issue description
New test code constructs `Text` with raw string literals (via type annotations) instead of `T.pack`.
## Issue Context
The test suite convention requires `T.pack "..."` when a `Text` value is intended.
## Fix Focus Areas
- test/API/ServerSpec.hs[109-111]
ⓘ Copy this prompt and use it to remediate the issue with your preferred AI generation tools
| - name: Start DB and API | ||
| run: | | ||
| docker-compose up -d db api | ||
| echo "Waiting for DB to be ready..." | ||
| for i in {1..60}; do | ||
| if docker-compose exec -T db pg_isready -U surypus -d surypus >/dev/null 2>&1; then echo 'DB ready'; break; fi | ||
| sleep 2 | ||
| done |
There was a problem hiding this comment.
4. Db readiness timeout ignored 🐞 Bug ☼ Reliability
CI and scripts wait for Postgres readiness but do not fail if the DB never becomes ready, so later build/tests can run against a down DB and fail non-deterministically.
Agent Prompt
### Issue description
The DB readiness loop can silently time out and continue, which makes failures later in the job misleading/flaky.
### Issue Context
Both the GitHub Actions workflow and `scripts/ci-runner.sh` do a bounded wait loop but don’t exit non-zero when Postgres never becomes ready.
### Fix Focus Areas
- .github/workflows/ci.yml[24-32]
- scripts/ci-runner.sh[13-20]
### Implementation notes
- Track a `ready=0/1` flag inside the loop (or check `pg_isready` once after the loop).
- If not ready after the loop, print a clear error and `exit 1`.
- (Optional) Dump `docker-compose logs db` on failure for faster diagnosis.
ⓘ Copy this prompt and use it to remediate the issue with your preferred AI generation tools
| "/api/v1/persons" | ||
| .= object | ||
| [ "get" | ||
| .= object | ||
| [ "summary" .= ("List persons" :: String), | ||
| "parameters" | ||
| .= object | ||
| [ "name" .= object ["type" .= ("string" :: String)], | ||
| "inn" .= object ["type" .= ("string" :: String)], | ||
| "type" .= object ["type" .= ("integer" :: String)], | ||
| "status" .= object ["type" .= ("integer" :: String)], | ||
| "limit" .= object ["type" .= ("integer" :: String)] | ||
| ], |
There was a problem hiding this comment.
5. Openapi parameters malformed 🐞 Bug ≡ Correctness
The generated swagger.json uses an object for operation "parameters" instead of an OpenAPI parameter array, so OpenAPI tooling may reject or misinterpret the spec.
Agent Prompt
### Issue description
swagger.json encodes `parameters` as an object map, not an OpenAPI parameters array, which breaks validators/codegen.
### Issue Context
Several operations include `"parameters" .= object [...]`.
### Fix Focus Areas
- src/Surypus/API/OpenApi.hs[160-172]
- src/Surypus/API/OpenApi.hs[200-233]
- src/Surypus/API/OpenApi.hs[383-433]
### Implementation notes
- Change each `"parameters"` value to a JSON array (`[ ... ]`).
- For query params, each entry should include at least `{ "name": "limit", "in": "query", "schema": {"type": "integer"} }`.
- For path params like `{id}`, mark `{ "name": "id", "in": "path", "required": true, "schema": {"type": "integer"} }`.
ⓘ Copy this prompt and use it to remediate the issue with your preferred AI generation tools
| "/api/v1/health/live" | ||
| .= object | ||
| [ "get" | ||
| .= object | ||
| [ "summary" .= ("Liveness probe" :: String), | ||
| "responses" | ||
| .= object | ||
| [ "200" .= object ["description" .= ("OK" :: String)] | ||
| ] | ||
| ] | ||
| ], | ||
| "/api/v1/health/ready" | ||
| .= object | ||
| [ "get" | ||
| .= object | ||
| [ "summary" .= ("Readiness probe" :: String), | ||
| "responses" | ||
| .= object | ||
| [ "200" .= object ["description" .= ("OK" :: String)] | ||
| ] | ||
| ] | ||
| ], |
There was a problem hiding this comment.
6. Swagger lists missing endpoints 🐞 Bug ≡ Correctness
swagger.json advertises /api/v1/health/live and /api/v1/health/ready, but the Servant API type only defines /api/v1/health, so clients will see endpoints that 404.
Agent Prompt
### Issue description
swagger.json documents health endpoints that the Servant API does not implement.
### Issue Context
This inconsistency makes the published spec unreliable for clients.
### Fix Focus Areas
- src/Surypus/API/OpenApi.hs[41-62]
- src/Surypus/API/Root.hs[200-203]
### Implementation notes
Choose one:
1) Remove `/api/v1/health/live` and `/api/v1/health/ready` from `apiSwaggerSpec`, or
2) Implement them in the Servant API by extending `HealthAPI` and adding handlers in `Surypus.API.Server`.
ⓘ Copy this prompt and use it to remediate the issue with your preferred AI generation tools
…docker, too slow)
There was a problem hiding this comment.
Actionable comments posted: 10
🧹 Nitpick comments (3)
test/Test.hs (1)
413-413: UseText.packfor the test label to follow repo test conventions.This line still uses a raw string literal for a test label.
Suggested adjustment
- prop "currency_rounding_in_bounds" prop_roundToPrecisionInBounds + prop (T.unpack (T.pack "currency_rounding_in_bounds")) prop_roundToPrecisionInBoundsAs per coding guidelines
test/**/*.hs: Always useText.packfor string literals in tests.🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed. In `@test/Test.hs` at line 413, Change the test label to use Text.pack to follow repo conventions: replace the raw string literal passed as the first argument to the test registration (the call using prop "currency_rounding_in_bounds" prop_roundToPrecisionInBounds) with Text.pack "currency_rounding_in_bounds" so the test label uses Text.pack while leaving the test property function prop_roundToPrecisionInBounds unchanged.test/RBACSpec.hs (1)
17-20: Consider normalizing the env flag value before comparison.Current checks handle
"1","true", and"TRUE"but miss variants like"True"or whitespace-padded values. A lowercase/trim normalization would make gating behavior less brittle.🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed. In `@test/RBACSpec.hs` around lines 17 - 20, The guard that checks the environment flag (the pattern "Just v | v == \"1\" || v == \"true\" || v == \"TRUE\"") is brittle; normalize v by trimming whitespace and lowercasing before comparison (e.g., compute a normalized value from v using trim and toLower) and then compare against "1" or "true" (or just check for "1" or "true") so values like " True " or "True" are handled; update the guard in RBACSpec.hs where the pattern matches Just v (and reference the OPENPAPYRUS_SKIP_RBAC_TESTS env var) to use the normalized value..github/workflows/ci.yml (1)
19-22: Consider upgradingdocker/setup-buildx-actionto v3 or v4 for latest features and security updates.
docker/setup-buildx-action@v2remains fully compatible with current GitHub-hosted runners, but is a legacy version (last updates ~2023, uses Node 16 runtime). Upgrading to v3 (current stable, Node 20) or v4 (latest, Node 24) provides access to newer features and security patches. If upgrading, test for any input changes between versions.Suggested change
- - name: Set up Docker Buildx - uses: docker/setup-buildx-action@v2 + - name: Set up Docker Buildx + uses: docker/setup-buildx-action@v3🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed. In @.github/workflows/ci.yml around lines 19 - 22, Update the GitHub Actions step named "Set up Docker Buildx" to use a newer action version by changing the uses string from docker/setup-buildx-action@v2 to docker/setup-buildx-action@v3 or `@v4` (prefer v4 for Node 24) and run the workflow to verify compatibility; after updating, review the step's inputs (e.g., install) against the v3/v4 README for any changed or deprecated inputs and adjust them accordingly, then test on a CI branch to confirm builds succeed.
🤖 Prompt for all review comments with AI agents
Verify each finding against the current code and only fix it if needed.
Inline comments:
In @.github/workflows/ci.yml:
- Around line 24-32: The "Start DB and API" step's readiness loop must exit
non-zero when Postgres never becomes ready; modify the block containing the
docker-compose up and the for loop that runs `docker-compose exec -T db
pg_isready ...` so that after the loop you detect if the loop exhausted all
attempts (e.g., via a readiness flag or checking the final pg_isready result)
and if so print an error like "DB did not become ready" and `exit 1` to fail the
job immediately instead of letting later steps fail with misleading errors.
In `@scripts/ci-runner.sh`:
- Around line 1-12: Add a cleanup trap so the compose stack is torn down on both
success and failure: define a function (e.g., cleanup) that runs docker-compose
down --remove-orphans (and optionally stops specific services) and ensure you
register it with trap 'cleanup' EXIT (and/or ERR) near the top of the script;
update references around docker-compose up -d db api and LOG_DIR/BUILD_LOG so
the cleanup runs regardless of how the script exits and does not rely on manual
invocation.
- Around line 13-20: The wait loop in scripts/ci-runner.sh that checks DB
readiness (for i in {1..60} ... docker-compose exec -T db pg_isready -U surypus
-d surypus) currently breaks on success but does not fail the script if the loop
completes without ever succeeding; modify the script to detect a timeout after
the loop (e.g., check whether the loop exited via break or not) and if the DB
never became ready echo an error to "$BUILD_LOG" and exit with a non-zero status
(exit 1) so downstream commands like stack build/test do not run when the DB is
unavailable.
In `@src/Core/Currency/Operations.hs`:
- Around line 95-98: The current code uses Prelude.round (banker's rounding) via
the expression "roundedInt = (round scaled) :: Integer"; replace this with a
custom half-up rounding helper (e.g. roundHalfUp :: Rational -> Integer) and
call it instead (e.g. roundedInt = roundHalfUp scaled). Implement roundHalfUp to
add a sign-aware 0.5 before flooring (for positive: floor (r + 0.5), for
negative: ceiling (r - 0.5) or equivalently floor (r + 0.5 * signum r)), ensure
it operates on Rational to avoid Double tie issues, and update the duplicated
algorithm in the other copy (the implementation in src/Core/Currency.hs around
the earlier rounding block) to use the same roundHalfUp function so both paths
perform half-up rounding.
In `@src/Surypus/API/OpenApi.hs`:
- Around line 165-172: The OpenApi parameter blocks currently emit "parameters"
as a JSON object keyed by name; update the generators in OpenApi.hs (the code
around the shown diff and the similar blocks at lines 205-232 and 388-431) to
emit an array of parameter objects instead: each parameter should be an object
with "name" (string), "in" (e.g. "query" or "path"), "schema" containing
{"type": "<type>"} and include "required": true for path params; locate the
functions building the path/item/parameters structures in OpenApi.hs and replace
the keyed-object construction with building a list/array of these parameter
objects so the produced /swagger.json conforms to OpenAPI 3 shape.
- Around line 41-62: The OpenApi spec lists "/api/v1/health/live" and
"/api/v1/health/ready" but the router in Root.hs only exposes "/api/v1/health",
causing mismatched docs and 404s; either remove those two paths from OpenApi.hs
or add corresponding routes and handlers in Root.hs. If you choose to add
routes, implement handlers (e.g., healthLiveHandler and healthReadyHandler) that
return 200 OK, and register them in the existing routing table/router where the
current health endpoint is defined so the OpenApi entries "/api/v1/health/live"
and "/api/v1/health/ready" match real endpoints. Ensure handler names and route
registrations match the exported symbols used by your router so the spec and
implementation stay in sync.
- Around line 3-7: Add Haddock documentation: add a module-level Haddock comment
above the "module Surypus.API.OpenApi ..." declaration describing the module's
purpose and public surface, and add a Haddock comment directly above the
exported function "apiSwaggerSpec" explaining what it returns (that it provides
the Swagger/OpenAPI spec as a Data.Aeson.Value), any important invariants or
inputs, and intended usage; ensure both comments use standard Haddock formatting
(-- | for top-level docs) and mention the module name Surypus.API.OpenApi and
the apiSwaggerSpec function.
In `@src/Surypus/API/Server.hs`:
- Around line 143-150: Replace direct logging of the raw username in the
authentication path by redacting or obfuscating it before calling
debugLogIf/debugLog: in the block that uses debugLogIf (pwd /= "admin123" && pwd
/= "demo") and the subsequent debugLog on success, remove the plain user string
and instead log a safe identifier (e.g. a constant placeholder like
"<redacted-user>", a truncated prefix, or a hash of user) so the calls that
remain—debugLogIf, debugLog, generateTokenPair, persistRefreshTokenBestEffort
and construction of LoginResponse/Surypus.JWT.accessToken/refreshToken—keep
current behavior but never emit the raw user value in logs.
In `@test/API/ServerSpec.hs`:
- Around line 266-284: The tests health_live_public and health_ready_public
currently accept 401 and 404 which lets them pass when the endpoints are
missing; update both test cases to assert that the returned status code is 200
only (remove the 401/404 acceptance), i.e. in the blocks that call mkTestApp and
runSession with jsonlessRequest for "/api/v1/health/live" and
"/api/v1/health/ready" replace the conditional that checks statusCode
(simpleStatus res) to require equality with 200 and call expectationFailure
otherwise, keeping the same helpers (mkTestApp, jsonlessRequest, runSession,
statusCode, simpleStatus).
- Around line 106-112: The test uses overloaded string literals for Text in
roleBody; update the rcrName and rcrPermissions entries to use Text.pack (e.g.
replace ("admin-test" :: Text) and (["goods:write"] :: [Text]) with Text.pack
"admin-test" and map Text.pack ["goods:write"] respectively), leave the empty
rcrResources as-is or ensure any non-empty resource strings are wrapped with
Text.pack; ensure Data.Text is imported qualified as Text if not already and
keep the surrounding encode/object usage (roleBody, rcrName, rcrPermissions,
rcrResources) unchanged.
---
Nitpick comments:
In @.github/workflows/ci.yml:
- Around line 19-22: Update the GitHub Actions step named "Set up Docker Buildx"
to use a newer action version by changing the uses string from
docker/setup-buildx-action@v2 to docker/setup-buildx-action@v3 or `@v4` (prefer v4
for Node 24) and run the workflow to verify compatibility; after updating,
review the step's inputs (e.g., install) against the v3/v4 README for any
changed or deprecated inputs and adjust them accordingly, then test on a CI
branch to confirm builds succeed.
In `@test/RBACSpec.hs`:
- Around line 17-20: The guard that checks the environment flag (the pattern
"Just v | v == \"1\" || v == \"true\" || v == \"TRUE\"") is brittle; normalize v
by trimming whitespace and lowercasing before comparison (e.g., compute a
normalized value from v using trim and toLower) and then compare against "1" or
"true" (or just check for "1" or "true") so values like " True " or "True" are
handled; update the guard in RBACSpec.hs where the pattern matches Just v (and
reference the OPENPAPYRUS_SKIP_RBAC_TESTS env var) to use the normalized value.
In `@test/Test.hs`:
- Line 413: Change the test label to use Text.pack to follow repo conventions:
replace the raw string literal passed as the first argument to the test
registration (the call using prop "currency_rounding_in_bounds"
prop_roundToPrecisionInBounds) with Text.pack "currency_rounding_in_bounds" so
the test label uses Text.pack while leaving the test property function
prop_roundToPrecisionInBounds unchanged.
🪄 Autofix (Beta)
Fix all unresolved CodeRabbit comments on this PR:
- Push a commit to this branch (recommended)
- Create a new PR with the fixes
ℹ️ Review info
⚙️ Run configuration
Configuration used: Organization UI
Review profile: CHILL
Plan: Pro
Run ID: d0735bd3-88ac-42e2-a025-bf023727b5be
📒 Files selected for processing (16)
.github/pull_request_template.md.github/workflows/ci.ymlREADME.mdSurypus.cabalapp/Main.hsscripts/ci-runner.shsrc/Core/Currency.hssrc/Core/Currency/Operations.hssrc/Surypus/API/AuthMiddleware.hssrc/Surypus/API/OpenApi.hssrc/Surypus/API/Root.hssrc/Surypus/API/Server.hssrc/Surypus/Logging.hstest/API/ServerSpec.hstest/RBACSpec.hstest/Test.hs
| - name: Start DB and API | ||
| run: | | ||
| docker-compose up -d db api | ||
| echo "Waiting for DB to be ready..." | ||
| for i in {1..60}; do | ||
| if docker-compose exec -T db pg_isready -U surypus -d surypus >/dev/null 2>&1; then echo 'DB ready'; break; fi | ||
| sleep 2 | ||
| done | ||
|
|
There was a problem hiding this comment.
Fail fast if Postgres never becomes ready.
If the loop exhausts all 60 attempts, this step still succeeds and the later build/test failure becomes a misleading downstream symptom. Exit non-zero after the timeout.
Suggested change
- name: Start DB and API
run: |
docker-compose up -d db api
echo "Waiting for DB to be ready..."
+ ready=0
for i in {1..60}; do
- if docker-compose exec -T db pg_isready -U surypus -d surypus >/dev/null 2>&1; then echo 'DB ready'; break; fi
+ if docker-compose exec -T db pg_isready -U surypus -d surypus >/dev/null 2>&1; then
+ echo 'DB ready'
+ ready=1
+ break
+ fi
sleep 2
done
+ if [ "$ready" -ne 1 ]; then
+ echo "DB did not become ready in time" >&2
+ docker-compose logs db >&2 || true
+ exit 1
+ fi📝 Committable suggestion
‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.
| - name: Start DB and API | |
| run: | | |
| docker-compose up -d db api | |
| echo "Waiting for DB to be ready..." | |
| for i in {1..60}; do | |
| if docker-compose exec -T db pg_isready -U surypus -d surypus >/dev/null 2>&1; then echo 'DB ready'; break; fi | |
| sleep 2 | |
| done | |
| - name: Start DB and API | |
| run: | | |
| docker-compose up -d db api | |
| echo "Waiting for DB to be ready..." | |
| ready=0 | |
| for i in {1..60}; do | |
| if docker-compose exec -T db pg_isready -U surypus -d surypus >/dev/null 2>&1; then | |
| echo 'DB ready' | |
| ready=1 | |
| break | |
| fi | |
| sleep 2 | |
| done | |
| if [ "$ready" -ne 1 ]; then | |
| echo "DB did not become ready in time" >&2 | |
| docker-compose logs db >&2 || true | |
| exit 1 | |
| fi |
🧰 Tools
🪛 actionlint (1.7.12)
[error] 25-25: shellcheck reported issue in this script: SC2034:warning:3:1: i appears unused. Verify use (or export if used externally)
(shellcheck)
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.
In @.github/workflows/ci.yml around lines 24 - 32, The "Start DB and API" step's
readiness loop must exit non-zero when Postgres never becomes ready; modify the
block containing the docker-compose up and the for loop that runs
`docker-compose exec -T db pg_isready ...` so that after the loop you detect if
the loop exhausted all attempts (e.g., via a readiness flag or checking the
final pg_isready result) and if so print an error like "DB did not become ready"
and `exit 1` to fail the job immediately instead of letting later steps fail
with misleading errors.
| #!/usr/bin/env bash | ||
| set -euo pipefail | ||
|
|
||
| LOG_DIR=${LOG_DIR:-logs} | ||
| BUILD_LOG="$LOG_DIR/ci_run_$(date +%Y%m%d_%H%M%S).log" | ||
| mkdir -p "$LOG_DIR" | ||
|
|
||
| echo "CI run started at $(date)" | tee -a "$BUILD_LOG" | ||
|
|
||
| echo "Starting services..." | tee -a "$BUILD_LOG" | ||
| docker-compose up -d db api >>"$BUILD_LOG" 2>&1 | ||
|
|
There was a problem hiding this comment.
Always tear down the compose stack.
The script starts db and api but never runs docker-compose down, so failed or repeated runs can leave stale containers behind and make later executions nondeterministic. Add a trap so cleanup happens on both success and failure.
Suggested change
#!/usr/bin/env bash
set -euo pipefail
+
+cleanup() {
+ docker-compose down >/dev/null 2>&1 || true
+}
+trap cleanup EXIT
LOG_DIR=${LOG_DIR:-logs}
BUILD_LOG="$LOG_DIR/ci_run_$(date +%Y%m%d_%H%M%S).log"Also applies to: 29-30
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.
In `@scripts/ci-runner.sh` around lines 1 - 12, Add a cleanup trap so the compose
stack is torn down on both success and failure: define a function (e.g.,
cleanup) that runs docker-compose down --remove-orphans (and optionally stops
specific services) and ensure you register it with trap 'cleanup' EXIT (and/or
ERR) near the top of the script; update references around docker-compose up -d
db api and LOG_DIR/BUILD_LOG so the cleanup runs regardless of how the script
exits and does not rely on manual invocation.
| echo "Waiting for DB readiness..." | tee -a "$BUILD_LOG" | ||
| for i in {1..60}; do | ||
| if docker-compose exec -T db pg_isready -U surypus -d surypus >/dev/null 2>&1; then | ||
| echo "DB is ready" | tee -a "$BUILD_LOG" | ||
| break | ||
| fi | ||
| sleep 2 | ||
| done |
There was a problem hiding this comment.
Fail the script when the database never becomes ready.
If all 60 attempts fail, the script just continues into stack build/stack test, so the real root cause is hidden. Exit non-zero after the wait loop.
Suggested change
echo "Waiting for DB readiness..." | tee -a "$BUILD_LOG"
+ready=0
for i in {1..60}; do
if docker-compose exec -T db pg_isready -U surypus -d surypus >/dev/null 2>&1; then
echo "DB is ready" | tee -a "$BUILD_LOG"
+ ready=1
break
fi
sleep 2
done
+
+if [ "$ready" -ne 1 ]; then
+ echo "DB did not become ready in time" | tee -a "$BUILD_LOG"
+ docker-compose logs db >>"$BUILD_LOG" 2>&1 || true
+ exit 1
+fi🧰 Tools
🪛 Shellcheck (0.11.0)
[warning] 14-14: i appears unused. Verify use (or export if used externally).
(SC2034)
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.
In `@scripts/ci-runner.sh` around lines 13 - 20, The wait loop in
scripts/ci-runner.sh that checks DB readiness (for i in {1..60} ...
docker-compose exec -T db pg_isready -U surypus -d surypus) currently breaks on
success but does not fail the script if the loop completes without ever
succeeding; modify the script to detect a timeout after the loop (e.g., check
whether the loop exited via break or not) and if the DB never became ready echo
an error to "$BUILD_LOG" and exit with a non-zero status (exit 1) so downstream
commands like stack build/test do not run when the DB is unavailable.
| amountR = toRational amount | ||
| scaled = amountR * (toRational factor) | ||
| roundedInt = (round scaled) :: Integer | ||
| in fromRational (roundedInt % factor) |
There was a problem hiding this comment.
🧩 Analysis chain
🏁 Script executed:
# First, let's examine the file structure and locate the exact files
find . -type f -name "*.hs" | grep -E "(Currency|Operations)" | head -20Repository: dominicusin/Surypus
Length of output: 446
🏁 Script executed:
# Read the specific lines mentioned in the review comment
echo "=== src/Core/Currency/Operations.hs lines 90-100 ==="
sed -n '90,100p' src/Core/Currency/Operations.hs
echo -e "\n=== src/Core/Currency/Operations.hs line 178 (QuickCheck property) ==="
sed -n '175,180p' src/Core/Currency/Operations.hsRepository: dominicusin/Surypus
Length of output: 769
🏁 Script executed:
# Check if duplicate logic exists in src/Core/Currency.hs
echo "=== src/Core/Currency.hs lines 60-75 ==="
sed -n '60,75p' src/Core/Currency.hsRepository: dominicusin/Surypus
Length of output: 658
🏁 Script executed:
# Check imports in both files to understand context
echo "=== Imports in src/Core/Currency/Operations.hs ==="
head -30 src/Core/Currency/Operations.hs
echo -e "\n=== Imports in src/Core/Currency.hs ==="
head -30 src/Core/Currency.hsRepository: dominicusin/Surypus
Length of output: 1589
🏁 Script executed:
# Check git log for commit messages related to this change
git log --oneline -20 | head -20Repository: dominicusin/Surypus
Length of output: 141
🏁 Script executed:
# Look for any TODO or FIXME comments related to rounding in both files
echo "=== TODO/FIXME in src/Core/Currency/Operations.hs ==="
grep -n "TODO\|FIXME\|XXX\|HACK\|BUG" src/Core/Currency/Operations.hs
echo -e "\n=== TODO/FIXME in src/Core/Currency.hs ==="
grep -n "TODO\|FIXME\|XXX\|HACK\|BUG" src/Core/Currency.hsRepository: dominicusin/Surypus
Length of output: 160
🏁 Script executed:
# Check if there's any documentation about rounding contract
echo "=== Function documentation for roundToPrecision ==="
sed -n '88,98p' src/Core/Currency/Operations.hs
echo -e "\n=== Function documentation for roundToCurrency ==="
sed -n '59,72p' src/Core/Currency.hsRepository: dominicusin/Surypus
Length of output: 1053
🏁 Script executed:
# Check the full context around both functions to see if there's any stated requirement
echo "=== Full roundToPrecision function context ==="
sed -n '85,110p' src/Core/Currency/Operations.hs
echo -e "\n=== Full roundToCurrency function context ==="
sed -n '59,75p' src/Core/Currency.hsRepository: dominicusin/Surypus
Length of output: 1742
round implements banker's rounding, not half-up rounding.
Line 97 uses Prelude.round, which rounds ties to even rather than half-up. With Double inputs, toRational preserves the binary approximation, so values like 1.005 are not rounded as decimal representations. The same algorithm is duplicated in src/Core/Currency.hs:63-72, so both rounding paths follow this behavior consistently.
If half-up rounding is required, both implementations need to be updated with a custom rounding function. If banker's rounding is acceptable, the functions match their documented bounds (± 0.5 * 10^(-precision)).
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.
In `@src/Core/Currency/Operations.hs` around lines 95 - 98, The current code uses
Prelude.round (banker's rounding) via the expression "roundedInt = (round
scaled) :: Integer"; replace this with a custom half-up rounding helper (e.g.
roundHalfUp :: Rational -> Integer) and call it instead (e.g. roundedInt =
roundHalfUp scaled). Implement roundHalfUp to add a sign-aware 0.5 before
flooring (for positive: floor (r + 0.5), for negative: ceiling (r - 0.5) or
equivalently floor (r + 0.5 * signum r)), ensure it operates on Rational to
avoid Double tie issues, and update the duplicated algorithm in the other copy
(the implementation in src/Core/Currency.hs around the earlier rounding block)
to use the same roundHalfUp function so both paths perform half-up rounding.
| module Surypus.API.OpenApi (apiSwaggerSpec) where | ||
|
|
||
| import Data.Aeson (Value, object, (.=)) | ||
|
|
||
| apiSwaggerSpec :: Value |
There was a problem hiding this comment.
Add Haddocks for the new public module surface.
This new module exports apiSwaggerSpec, but it does not have the required module header or a public-function doc comment.
As per coding guidelines "Include module header documentation (required for all modules)" and "Include function documentation for public API functions".
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.
In `@src/Surypus/API/OpenApi.hs` around lines 3 - 7, Add Haddock documentation:
add a module-level Haddock comment above the "module Surypus.API.OpenApi ..."
declaration describing the module's purpose and public surface, and add a
Haddock comment directly above the exported function "apiSwaggerSpec" explaining
what it returns (that it provides the Swagger/OpenAPI spec as a
Data.Aeson.Value), any important invariants or inputs, and intended usage;
ensure both comments use standard Haddock formatting (-- | for top-level docs)
and mention the module name Surypus.API.OpenApi and the apiSwaggerSpec function.
| "/api/v1/health/live" | ||
| .= object | ||
| [ "get" | ||
| .= object | ||
| [ "summary" .= ("Liveness probe" :: String), | ||
| "responses" | ||
| .= object | ||
| [ "200" .= object ["description" .= ("OK" :: String)] | ||
| ] | ||
| ] | ||
| ], | ||
| "/api/v1/health/ready" | ||
| .= object | ||
| [ "get" | ||
| .= object | ||
| [ "summary" .= ("Readiness probe" :: String), | ||
| "responses" | ||
| .= object | ||
| [ "200" .= object ["description" .= ("OK" :: String)] | ||
| ] | ||
| ] | ||
| ], |
There was a problem hiding this comment.
Don't advertise health probes that are not routed.
This spec now publishes /api/v1/health/live and /api/v1/health/ready, but src/Surypus/API/Root.hs only exposes /api/v1/health. Clients following the spec will hit 404 until the corresponding routes and handlers are added.
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.
In `@src/Surypus/API/OpenApi.hs` around lines 41 - 62, The OpenApi spec lists
"/api/v1/health/live" and "/api/v1/health/ready" but the router in Root.hs only
exposes "/api/v1/health", causing mismatched docs and 404s; either remove those
two paths from OpenApi.hs or add corresponding routes and handlers in Root.hs.
If you choose to add routes, implement handlers (e.g., healthLiveHandler and
healthReadyHandler) that return 200 OK, and register them in the existing
routing table/router where the current health endpoint is defined so the OpenApi
entries "/api/v1/health/live" and "/api/v1/health/ready" match real endpoints.
Ensure handler names and route registrations match the exported symbols used by
your router so the spec and implementation stay in sync.
| "parameters" | ||
| .= object | ||
| [ "name" .= object ["type" .= ("string" :: String)], | ||
| "inn" .= object ["type" .= ("string" :: String)], | ||
| "type" .= object ["type" .= ("integer" :: String)], | ||
| "status" .= object ["type" .= ("integer" :: String)], | ||
| "limit" .= object ["type" .= ("integer" :: String)] | ||
| ], |
There was a problem hiding this comment.
parameters is encoded in a non-OpenAPI shape.
These blocks emit parameters as JSON objects keyed by parameter name, but OpenAPI 3 expects an array of parameter objects with name, in, and schema fields (required is also mandatory for path params). As written, /swagger.json returns JSON, but not a spec most OpenAPI tooling can validate or consume.
Example of the shape OpenAPI expects
- "parameters" .= object
- [ "id" .= object ["type" .= ("integer" :: String)]
- ],
+ "parameters" .=
+ [ object
+ [ "name" .= ("id" :: String),
+ "in" .= ("path" :: String),
+ "required" .= (True :: Bool),
+ "schema" .= object ["type" .= ("integer" :: String)]
+ ]
+ ],Also applies to: 205-232, 388-431
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.
In `@src/Surypus/API/OpenApi.hs` around lines 165 - 172, The OpenApi parameter
blocks currently emit "parameters" as a JSON object keyed by name; update the
generators in OpenApi.hs (the code around the shown diff and the similar blocks
at lines 205-232 and 388-431) to emit an array of parameter objects instead:
each parameter should be an object with "name" (string), "in" (e.g. "query" or
"path"), "schema" containing {"type": "<type>"} and include "required": true for
path params; locate the functions building the path/item/parameters structures
in OpenApi.hs and replace the keyed-object construction with building a
list/array of these parameter objects so the produced /swagger.json conforms to
OpenAPI 3 shape.
| liftIO $ debugLogIf (pwd /= "admin123" && pwd /= "demo") $ "Login failed for user: " <> user | ||
| if pwd == "admin123" || pwd == "demo" | ||
| then do | ||
| tokenResult <- liftIO $ generateTokenPair (envJWTConfig env) 1 user "admin" | ||
| let tp = tokenResult | ||
| liftIO $ persistRefreshTokenBestEffort env 1 (Surypus.JWT.refreshToken tp) | ||
| liftIO $ debugLog $ "Login succeeded for user: " <> user | ||
| pure LoginResponse {accessToken = Surypus.JWT.accessToken tp, refreshToken = Surypus.JWT.refreshToken tp, userId = 1, userName = user, role = "admin"} |
There was a problem hiding this comment.
Avoid logging raw usernames in auth success/failure paths.
These debug messages include user identifiers directly. Even with env-gated debug mode, this weakens privacy posture and can aid account-enumeration analysis in leaked logs.
Safer logging variant
- liftIO $ debugLogIf (pwd /= "admin123" && pwd /= "demo") $ "Login failed for user: " <> user
+ liftIO $ debugLogIf (pwd /= "admin123" && pwd /= "demo") "Login failed"
...
- liftIO $ debugLog $ "Login succeeded for user: " <> user
+ liftIO $ debugLog "Login succeeded"📝 Committable suggestion
‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.
| liftIO $ debugLogIf (pwd /= "admin123" && pwd /= "demo") $ "Login failed for user: " <> user | |
| if pwd == "admin123" || pwd == "demo" | |
| then do | |
| tokenResult <- liftIO $ generateTokenPair (envJWTConfig env) 1 user "admin" | |
| let tp = tokenResult | |
| liftIO $ persistRefreshTokenBestEffort env 1 (Surypus.JWT.refreshToken tp) | |
| liftIO $ debugLog $ "Login succeeded for user: " <> user | |
| pure LoginResponse {accessToken = Surypus.JWT.accessToken tp, refreshToken = Surypus.JWT.refreshToken tp, userId = 1, userName = user, role = "admin"} | |
| liftIO $ debugLogIf (pwd /= "admin123" && pwd /= "demo") "Login failed" | |
| if pwd == "admin123" || pwd == "demo" | |
| then do | |
| tokenResult <- liftIO $ generateTokenPair (envJWTConfig env) 1 user "admin" | |
| let tp = tokenResult | |
| liftIO $ persistRefreshTokenBestEffort env 1 (Surypus.JWT.refreshToken tp) | |
| liftIO $ debugLog "Login succeeded" | |
| pure LoginResponse {accessToken = Surypus.JWT.accessToken tp, refreshToken = Surypus.JWT.refreshToken tp, userId = 1, userName = user, role = "admin"} |
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.
In `@src/Surypus/API/Server.hs` around lines 143 - 150, Replace direct logging of
the raw username in the authentication path by redacting or obfuscating it
before calling debugLogIf/debugLog: in the block that uses debugLogIf (pwd /=
"admin123" && pwd /= "demo") and the subsequent debugLog on success, remove the
plain user string and instead log a safe identifier (e.g. a constant placeholder
like "<redacted-user>", a truncated prefix, or a hash of user) so the calls that
remain—debugLogIf, debugLog, generateTokenPair, persistRefreshTokenBestEffort
and construction of LoginResponse/Surypus.JWT.accessToken/refreshToken—keep
current behavior but never emit the raw user value in logs.
| let roleBody = | ||
| encode $ | ||
| object | ||
| [ "rcrName" .= ("admin-test" :: Text), | ||
| "rcrPermissions" .= (["goods:write"] :: [Text]), | ||
| "rcrResources" .= ([] :: [Maybe Text]) | ||
| ] |
There was a problem hiding this comment.
Use Text.pack for the new test Text literals.
This file is under test/**/*.hs, and the new RBAC payload still relies on overloaded string literals for Text values.
Suggested change
object
- [ "rcrName" .= ("admin-test" :: Text),
- "rcrPermissions" .= (["goods:write"] :: [Text]),
+ [ "rcrName" .= T.pack "admin-test",
+ "rcrPermissions" .= [T.pack "goods:write"],
"rcrResources" .= ([] :: [Maybe Text])
]📝 Committable suggestion
‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.
| let roleBody = | |
| encode $ | |
| object | |
| [ "rcrName" .= ("admin-test" :: Text), | |
| "rcrPermissions" .= (["goods:write"] :: [Text]), | |
| "rcrResources" .= ([] :: [Maybe Text]) | |
| ] | |
| let roleBody = | |
| encode $ | |
| object | |
| [ "rcrName" .= T.pack "admin-test", | |
| "rcrPermissions" .= [T.pack "goods:write"], | |
| "rcrResources" .= ([] :: [Maybe Text]) | |
| ] |
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.
In `@test/API/ServerSpec.hs` around lines 106 - 112, The test uses overloaded
string literals for Text in roleBody; update the rcrName and rcrPermissions
entries to use Text.pack (e.g. replace ("admin-test" :: Text) and
(["goods:write"] :: [Text]) with Text.pack "admin-test" and map Text.pack
["goods:write"] respectively), leave the empty rcrResources as-is or ensure any
non-empty resource strings are wrapped with Text.pack; ensure Data.Text is
imported qualified as Text if not already and keep the surrounding encode/object
usage (roleBody, rcrName, rcrPermissions, rcrResources) unchanged.
| it "health_live_public" $ do | ||
| app <- mkTestApp | ||
| res <- runSession (srequest $ jsonlessRequest methodGet "/api/v1/health/live" []) app | ||
| let code = statusCode (simpleStatus res) | ||
| if code == 200 || code == 401 || code == 404 | ||
| then return () | ||
| else expectationFailure $ "health live returned status " ++ show code | ||
|
|
||
| it "health_ready_public" $ do | ||
| mbSkip <- lookupEnv "OPENPAPYRUS_SKIP_READY_HEALTH" | ||
| case mbSkip of | ||
| Just "1" -> return () | ||
| _ -> do | ||
| app <- mkTestApp | ||
| res <- runSession (srequest $ jsonlessRequest methodGet "/api/v1/health/ready" []) app | ||
| let code = statusCode (simpleStatus res) | ||
| if code == 200 || code == 401 || code == 404 | ||
| then return () | ||
| else expectationFailure $ "health/ready endpoint returned status " ++ show code |
There was a problem hiding this comment.
These probe tests pass even when the endpoint is missing.
Lines 270-283 accept 401 and 404, so the suite stays green if the route disappears or stops being public. That makes the new coverage ineffective for the public probe contract.
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.
In `@test/API/ServerSpec.hs` around lines 266 - 284, The tests health_live_public
and health_ready_public currently accept 401 and 404 which lets them pass when
the endpoints are missing; update both test cases to assert that the returned
status code is 200 only (remove the 401/404 acceptance), i.e. in the blocks that
call mkTestApp and runSession with jsonlessRequest for "/api/v1/health/live" and
"/api/v1/health/ready" replace the conditional that checks statusCode
(simpleStatus res) to require equality with 200 and call expectationFailure
otherwise, keeping the same helpers (mkTestApp, jsonlessRequest, runSession,
statusCode, simpleStatus).
There was a problem hiding this comment.
Actionable comments posted: 2
♻️ Duplicate comments (1)
.github/workflows/ci.yml (1)
33-36:⚠️ Potential issue | 🟠 MajorFail fast when Postgres never becomes ready.
Line [33]–Line [36] still exits successfully even if DB readiness never succeeds, which masks the root cause and produces misleading downstream failures.
Suggested fix
- name: Start DB run: | docker compose up -d db echo "Waiting for DB to be ready..." + ready=0 for i in {1..60}; do - if docker compose exec -T db pg_isready -U surypus -d surypus >/dev/null 2>&1; then echo 'DB ready'; break; fi + if docker compose exec -T db pg_isready -U surypus -d surypus >/dev/null 2>&1; then + echo 'DB ready' + ready=1 + break + fi sleep 2 done + if [ "$ready" -ne 1 ]; then + echo "DB did not become ready in time" >&2 + docker compose logs db >&2 || true + exit 1 + fi🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed. In @.github/workflows/ci.yml around lines 33 - 36, The readiness loop using "docker compose exec -T db pg_isready -U surypus -d surypus" currently breaks only on success but will let the job continue if the loop times out; add an explicit failure path after the for loop that detects a timed-out/unready DB, prints an error like "DB failed to become ready" and exits non‑zero (exit 1) so the workflow fails fast when Postgres never becomes ready.
🤖 Prompt for all review comments with AI agents
Verify each finding against the current code and only fix it if needed.
Inline comments:
In @.github/workflows/ci.yml:
- Around line 42-44: The "Run Tests" CI step currently runs stack test without
setting the RBAC gating variable; update the "Run Tests" job/step named "Run
Tests" so it applies OPENPAPYRUS_SKIP_RBAC_TESTS=1 to the test invocation
(either by adding an env: OPENPAPYRUS_SKIP_RBAC_TESTS: '1' on that step or
prefixing the command with the environment variable), ensuring the stack test
execution honors the RBAC test-gating flag.
- Line 20: Update the GitHub Action invocation string "uses:
docker/setup-buildx-action@v2" to the current recommended major version by
changing it to "uses: docker/setup-buildx-action@v4"; locate the uses entry in
the CI workflow and replace the version tag while keeping any existing
inputs/with settings unchanged to ensure compatibility.
---
Duplicate comments:
In @.github/workflows/ci.yml:
- Around line 33-36: The readiness loop using "docker compose exec -T db
pg_isready -U surypus -d surypus" currently breaks only on success but will let
the job continue if the loop times out; add an explicit failure path after the
for loop that detects a timed-out/unready DB, prints an error like "DB failed to
become ready" and exits non‑zero (exit 1) so the workflow fails fast when
Postgres never becomes ready.
🪄 Autofix (Beta)
Fix all unresolved CodeRabbit comments on this PR:
- Push a commit to this branch (recommended)
- Create a new PR with the fixes
ℹ️ Review info
⚙️ Run configuration
Configuration used: Organization UI
Review profile: CHILL
Plan: Pro
Run ID: 1130001f-b6e3-45d0-a3d9-79a85fb27e56
📒 Files selected for processing (3)
.github/pull_request_template.md.github/workflows/ci.ymlREADME.md
✅ Files skipped from review due to trivial changes (1)
- README.md
🚧 Files skipped from review as they are similar to previous changes (1)
- .github/pull_request_template.md
| run: | | ||
| hlint src/ | ||
| - name: Set up Docker Buildx | ||
| uses: docker/setup-buildx-action@v2 |
There was a problem hiding this comment.
🧩 Analysis chain
🌐 Web query:
Is docker/setup-buildx-action@v2 still supported on GitHub Actions runners in 2026, and what is the recommended current major version?
💡 Result:
docker/setup-buildx-action@v2 is still supported on GitHub Actions runners in 2026 as older tagged versions of GitHub Actions remain available and functional indefinitely, though it is not the recommended version. The recommended current major version is v4, with the latest release v4.0.0 on 2026-03-05. Official Docker documentation and examples consistently use v4, and the repository's README and Marketplace promote v4.
Citations:
- 1: https://github.com/docker/setup-buildx-action
- 2: https://github.com/marketplace/actions/docker-setup-buildx
- 3: https://docs.docker.com/build/ci/github-actions/configure-builder
- 4: https://docs.docker.com/build/ci/github-actions/multi-platform/
- 5: https://github.com/docker/setup-buildx-action/releases
- 6: https://github.com/docker/setup-buildx-action/
- 7: https://github.com/docker/setup-buildx-action/releases/latest
Update docker/setup-buildx-action from @v2 to @v4 (current recommended version).
Line 20 uses docker/setup-buildx-action@v2, which is outdated. The current recommended major version is v4 (released March 2026). While v2 remains functional, updating ensures compatibility with current GitHub Actions best practices and Docker's official recommendations.
Suggested fix
- - name: Set up Docker Buildx
- uses: docker/setup-buildx-action@v2
+ - name: Set up Docker Buildx
+ uses: docker/setup-buildx-action@v4
with:
install: true📝 Committable suggestion
‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.
| uses: docker/setup-buildx-action@v2 | |
| - name: Set up Docker Buildx | |
| uses: docker/setup-buildx-action@v4 | |
| with: | |
| install: true |
🧰 Tools
🪛 actionlint (1.7.12)
[error] 20-20: the runner of "docker/setup-buildx-action@v2" action is too old to run on GitHub Actions. update the action's version to fix this issue
(action)
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.
In @.github/workflows/ci.yml at line 20, Update the GitHub Action invocation
string "uses: docker/setup-buildx-action@v2" to the current recommended major
version by changing it to "uses: docker/setup-buildx-action@v4"; locate the uses
entry in the CI workflow and replace the version tag while keeping any existing
inputs/with settings unchanged to ensure compatibility.
| - name: Run Tests | ||
| run: | | ||
| stack test |
There was a problem hiding this comment.
Apply RBAC test-gating env var in CI test step.
Line [42]–Line [44] runs stack test without OPENPAPYRUS_SKIP_RBAC_TESTS=1, so the PR’s CI-gating objective is not actually enforced here.
Suggested fix
- name: Run Tests
+ env:
+ OPENPAPYRUS_SKIP_RBAC_TESTS: "1"
run: |
stack test🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.
In @.github/workflows/ci.yml around lines 42 - 44, The "Run Tests" CI step
currently runs stack test without setting the RBAC gating variable; update the
"Run Tests" job/step named "Run Tests" so it applies
OPENPAPYRUS_SKIP_RBAC_TESTS=1 to the test invocation (either by adding an env:
OPENPAPYRUS_SKIP_RBAC_TESTS: '1' on that step or prefixing the command with the
environment variable), ensuring the stack test execution honors the RBAC
test-gating flag.
Summary
Phase 1: Stabilize core infrastructure — CI gating, real OpenAPI, debug logging, test fixes.
Motivation
The CI pipeline was blocked by:
Changes
CI Gating
.github/workflows/ci.yml: AddedOPENPAPYRUS_SKIP_RBAC_TESTS=1to test step. Swagger gating removed (real OpenAPI now served).scripts/ci-runner.sh: Synced with CI workflow.Real OpenAPI
src/Surypus/API/OpenApi.hs(NEW): Real OpenAPI 3.0.3 spec as aValue— covers auth, persons, goods, locations, bills, rbac, audit, health, metrics endpoints.src/Surypus/API/Root.hs:apiSwagger = apiSwaggerSpec(was placeholder string).Surypus.cabal: AddedSurypus.API.OpenApitoexposed-modules.Debug Logging
src/Surypus/Logging.hs: AddeddebugLog :: Text -> IO ()anddebugLogIf :: Bool -> Text -> IO ()— checkOPENPAPYRUS_DEBUG=1.src/Surypus/API/AuthMiddleware.hs: Replaced localdebugLogwith centralized import fromSurypus.Logging.src/Surypus/API/Server.hs: Debug output on login success/failure, health check DB failure, server startup.Test Fixes
test/RBACSpec.hs: Full rewrite — gating viaOPENPAPYRUS_SKIP_RBAC_TESTSatmainlevel; correctdescribe "RBAC" $ doindentation.test/API/ServerSpec.hs: Restored 2 malformeddoblocks ("active grants", "update dynamic role"); added/swagger.jsontopublicPaths; removed Swagger gating.Documentation
README.md: Added "CI gating" and "Debug logging (OPENPAPYRUS_DEBUG)" sections.Testing
Local test commands
Results
Test groups
Risks
src/Surypus/API/OpenApi.hsas needed.Environment Variables
OPENPAPYRUS_SKIP_RBAC_TESTSOPENPAPYRUS_DEBUG0Checklist
stack build --fast, no errors, no warnings)/swagger.jsonOPENPAPYRUS_DEBUG=1produces debug outputSummary by CodeRabbit
New Features
Improvements
Bug Fixes
Tests
Documentation