packages feed

keiro-ops 0.16.0.0 → 0.17.0.0

raw patch · 4 files changed

+156/−70 lines, 4 filesdep ~keirodep ~keiro-migrationsdep ~keiro-pgmqPVP ok

version bump matches the API change (PVP)

Dependency ranges changed: keiro, keiro-migrations, keiro-pgmq, keiro-test-support, pgmq-migration

API changes (from Hackage documentation)

Files

CHANGELOG.md view
@@ -6,6 +6,20 @@  ## [Unreleased] +## 0.17.0.0 — 2026-09-17++### Breaking Changes++- Forced `pgmq dlq purge` execution now calls the visibility-guarded library API,+  reports the actual deleted count, and fails without deleting when metrics show+  hidden rows from an inspection. The global `--force` flag still authorizes the+  mutation after preview; it does not bypass this safety refusal.++### Other Changes++- No user-facing library changes. The test-suite's `pgmq-migration` bound moves+  from `>=0.5 && <0.6` to `>=0.6 && <0.7` with the PGMQ 0.6 adoption.+ ## 0.16.0.0 — 2026-09-07  ### Other Changes
keiro-ops.cabal view
@@ -1,34 +1,39 @@-cabal-version:   3.0-name:            keiro-ops-version:         0.16.0.0-synopsis:        Operational command-line interface for Keiro deployments+cabal-version: 3.0+name: keiro-ops+version: 0.17.0.0+synopsis: Operational command-line interface for Keiro deployments description:   A standalone database-only console and embeddable command tree for inspecting   and operating Keiro deployments. -license:         BSD-3-Clause-license-file:    LICENSE-author:          Nadeem Bitar-maintainer:      nadeem@gmail.com-copyright:       2026 Nadeem Bitar-category:        Operations-homepage:        https://github.com/shinzui/keiro#readme-bug-reports:     https://github.com/shinzui/keiro/issues-build-type:      Simple-tested-with:     GHC >=9.12 && <9.13+license: BSD-3-Clause+license-file: LICENSE+author: Nadeem Bitar+maintainer: nadeem@gmail.com+copyright: 2026 Nadeem Bitar+category: Operations+homepage: https://github.com/shinzui/keiro#readme+bug-reports: https://github.com/shinzui/keiro/issues+build-type: Simple+tested-with: ghc >=9.12 && <9.13 extra-doc-files: CHANGELOG.md  source-repository head-  type:     git+  type: git   location: https://github.com/shinzui/keiro.git  common warnings   ghc-options:-    -Wall -Wcompat -Widentities -Wincomplete-record-updates-    -Wincomplete-uni-patterns -Wpartial-fields -Wredundant-constraints+    -Wall+    -Wcompat+    -Widentities+    -Wincomplete-record-updates+    -Wincomplete-uni-patterns+    -Wpartial-fields+    -Wredundant-constraints  common shared-  default-language:   GHC2024+  default-language: GHC2024   default-extensions:     BlockArguments     DeriveAnyClass@@ -40,8 +45,8 @@     OverloadedStrings  library-  import:          warnings, shared-  hs-source-dirs:  src+  import: warnings, shared+  hs-source-dirs: src   exposed-modules:     Keiro.Ops     Keiro.Ops.Embed@@ -61,58 +66,66 @@     Keiro.Ops.Workflow    build-depends:-    , aeson                 >=2.2.2     && <2.3-    , base                  >=4.21      && <5-    , bytestring            >=0.12      && <0.13-    , containers            >=0.6       && <0.8-    , effectful             >=2.6       && <2.7-    , effectful-core        >=2.6       && <2.7-    , hasql                 >=1.10      && <1.11-    , keiro                 ^>=0.16.0.0-    , keiro-migrations      ^>=0.16.0.0-    , keiro-pgmq            ^>=0.16.0.0-    , kiroku-store          >=0.8       && <0.9-    , optparse-applicative  >=0.18      && <0.20-    , text                  >=2.1       && <2.2-    , time                  >=1.12      && <1.15-    , uuid                  >=1.3       && <1.4-    , vector                >=0.13      && <0.14+    aeson >=2.2.2 && <2.3,+    base >=4.21 && <5,+    bytestring >=0.12 && <0.13,+    containers >=0.6 && <0.8,+    effectful >=2.6 && <2.7,+    effectful-core >=2.6 && <2.7,+    hasql >=1.10 && <1.11,+    keiro ^>=0.17.0.0,+    keiro-migrations ^>=0.17.0.0,+    keiro-pgmq ^>=0.17.0.0,+    kiroku-store >=0.8 && <0.9,+    optparse-applicative >=0.18 && <0.20,+    text >=2.1 && <2.2,+    time >=1.12 && <1.15,+    uuid >=1.3 && <1.4,+    vector >=0.13 && <0.14,  executable keiro-ops-  import:         warnings, shared+  import: warnings, shared   hs-source-dirs: app-  main-is:        Main.hs-  ghc-options:    -threaded -rtsopts -with-rtsopts=-N+  main-is: Main.hs+  ghc-options:+    -threaded+    -rtsopts+    -with-rtsopts=-N+   build-depends:-    , base       >=4.21 && <5-    , keiro-ops+    base >=4.21 && <5,+    keiro-ops,  test-suite keiro-ops-test-  import:             warnings, shared-  type:               exitcode-stdio-1.0-  hs-source-dirs:     test-  main-is:            Main.hs-  ghc-options:        -threaded -rtsopts -with-rtsopts=-N+  import: warnings, shared+  type: exitcode-stdio-1.0+  hs-source-dirs: test+  main-is: Main.hs+  ghc-options:+    -threaded+    -rtsopts+    -with-rtsopts=-N+   build-tool-depends: keiro-ops:keiro-ops   build-depends:-    , aeson                 >=2.2       && <2.3-    , base                  >=4.21      && <5-    , bytestring            >=0.12      && <0.13-    , containers            >=0.6       && <0.8-    , effectful             >=2.6       && <2.7-    , effectful-core        >=2.6       && <2.7-    , hasql                 >=1.10      && <1.11-    , hasql-transaction     >=1.1       && <1.3-    , hspec                 >=2.11-    , keiro                 ^>=0.16.0.0-    , keiro-ops-    , keiro-pgmq            ^>=0.16.0.0-    , keiro-test-support    ^>=0.16.0.0-    , kiroku-store          >=0.8       && <0.9-    , optparse-applicative  >=0.18      && <0.20-    , pgmq-migration        >=0.5       && <0.6-    , process               >=1.6       && <1.7-    , text                  >=2.1       && <2.2-    , time                  >=1.12      && <1.15-    , uuid                  >=1.3       && <1.4-    , vector                >=0.13      && <0.14+    aeson >=2.2 && <2.3,+    base >=4.21 && <5,+    bytestring >=0.12 && <0.13,+    containers >=0.6 && <0.8,+    effectful >=2.6 && <2.7,+    effectful-core >=2.6 && <2.7,+    hasql >=1.10 && <1.11,+    hasql-transaction >=1.1 && <1.3,+    hspec >=2.11,+    keiro ^>=0.17.0.0,+    keiro-ops,+    keiro-pgmq ^>=0.17.0.0,+    keiro-test-support ^>=0.17.0.0,+    kiroku-store >=0.8 && <0.9,+    optparse-applicative >=0.18 && <0.20,+    pgmq-migration >=0.6 && <0.7,+    process >=1.6 && <1.7,+    text >=2.1 && <2.2,+    time >=1.12 && <1.15,+    uuid >=1.3 && <1.4,+    vector >=0.13 && <0.14,
src/Keiro/Ops/Pgmq.hs view
@@ -127,8 +127,15 @@   | env.force = do       confirmed <- confirmPurge env queue       if confirmed-        then handlePgmq (runJobEff (pgmqRuntime env) (purgeDlq (rawJob queue))) $ \() ->-          Succeeded (messageResult ("purged DLQ for " <> queue))+        then handlePgmq (runJobEff (pgmqRuntime env) (purgeDlq (rawJob queue))) \case+          PurgeDlqBlocked hiddenCount ->+            Failed+              ( "DLQ purge refused because "+                  <> showText hiddenCount+                  <> " row(s) are hidden; archive inspected ids or wait for visibility to expire"+              )+          PurgeDlqPurged purged ->+            Succeeded (mutationCountResult "purge" queue purged)         else pure (Failed "queue-name confirmation did not match; DLQ purge cancelled")   | otherwise = previewFromDepth env "purge" queue Nothing ["pgmq", "dlq", "purge", "--queue", queue] @@ -174,6 +181,7 @@     { jobName = queue,       jobQueue = queueRef queue,       jobCodec = aesonJobCodec,+      jobOrdering = Unordered,       jobPolicy = defaultRetryPolicy     } @@ -206,10 +214,11 @@       "original_message_id" .= entry.originalMessageId,       "original_enqueued_at" .= entry.originalEnqueuedAt,       "read_count" .= entry.readCount,+      "original_headers" .= entry.originalHeaders,       "raw_body" .= entry.rawBody     ] -mutationCountResult :: Text -> Text -> Int -> OpsResult+mutationCountResult :: (Show count, Aeson.ToJSON count) => Text -> Text -> count -> OpsResult mutationCountResult operation queue count =   OpsResult     { headers = ["operation", "queue", "affected"],
test/Main.hs view
@@ -1159,9 +1159,58 @@        purged <- OpsPgmq.runCommand (opsEnv True store) (OpsPgmq.Dlq (OpsPgmq.Purge queue))       purged `shouldSatisfy` isSucceeded+      jsonInteger "affected" purged `shouldBe` Just 1       (_, dlqAfterPurge) <- depths       dlqAfterPurge `shouldBe` 0 +    it "refuses purge after inspection and leaves the DLQ unchanged" $ \store -> do+      let queue = "keiro_ops_test.purge_hidden"+          job = rawValueJob queue+          runPgmqUnit action = do+            result <- runJobEff (JobRuntime store.pool Nothing) action+            either (fail . show) pure result+          dlqDepth = do+            result <- runJobEff (JobRuntime store.pool Nothing) (jobDlqMetrics job)+            metrics <- either (fail . show) pure result+            pure metrics.queueLength+      inspectedCount <- runPgmqUnit $ do+        ensureJobQueue job+        _ <- enqueue job (object ["kind" .= ("inspect-first" :: Text)])+        _ <- runJobOnce 1 job (\_ -> pure (Dead "bad"))+        inspected <- readDlq job 1+        pure (length inspected)+      inspectedCount `shouldBe` 1++      outcome <- OpsPgmq.runCommand (opsEnv True store) (OpsPgmq.Dlq (OpsPgmq.Purge queue))+      case outcome of+        Failed detail -> do+          detail `shouldSatisfy` Text.isInfixOf "1 row(s) are hidden"+          detail `shouldSatisfy` Text.isInfixOf "archive inspected ids"+        other -> expectationFailure ("expected guarded purge refusal, got " <> show other)+      dlqDepth `shouldReturn` 1++    it "archives an inspected entry by id while it remains hidden" $ \store -> do+      let queue = "keiro_ops_test.archive_hidden"+          job = rawValueJob queue+          runPgmqUnit action = do+            result <- runJobEff (JobRuntime store.pool Nothing) action+            either (fail . show) pure result+          dlqDepth = do+            result <- runJobEff (JobRuntime store.pool Nothing) (jobDlqMetrics job)+            metrics <- either (fail . show) pure result+            pure metrics.queueLength+      inspectedCount <- runPgmqUnit $ do+        ensureJobQueue job+        _ <- enqueue job (object ["kind" .= ("retain" :: Text)])+        _ <- runJobOnce 1 job (\_ -> pure (Dead "bad"))+        inspected <- readDlq job 1+        pure (length inspected)+      inspectedCount `shouldBe` 1++      archived <- OpsPgmq.runCommand (opsEnv True store) (OpsPgmq.Dlq (OpsPgmq.Archive queue (Just 1) 100))+      archived `shouldSatisfy` isSucceeded+      dlqDepth `shouldReturn` 0+   describe "projection handlers" $ around (withFreshStore fixture) do     it "prunes only the named dedup rows" $ \store -> do       _ <- seedKirokuEvent store "projection-source" "018f5f43-8a70-7b9a-9a9b-59d391a76810" Nothing@@ -1360,6 +1409,7 @@     { jobName = name,       jobQueue = queueRef name,       jobCodec = aesonJobCodec,+      jobOrdering = Unordered,       jobPolicy = defaultRetryPolicy     }