From d25e3abff6a20f1c4813887750511a367f908bb4 Mon Sep 17 00:00:00 2001 From: Paul Burns Date: Tue, 18 Aug 2026 19:46:25 -0400 Subject: [PATCH 1/5] Add the ci flag and fix every warning it reports Follows bounded-text, beeline, shrubbery, orb, rollbar-haskell and haskell-non-empty-text, which all carry a manual ci flag holding the strict ghc-options. Two deviations from that list: no henforcer plugin, since this repo has no henforcer.toml and its open imports would fail immediately, and no -Wmissing-import-lists, which would rewrite thirteen imports in the file PR #2 also edits. The flag is manual and defaults off, so nothing reaches Hackage consumers. It is enabled for all three packages in stack-base.yaml, so every rung of the matrix builds with -Werror. Libraries get no ghc-options at all when the flag is off, as before -- an -O2 in the else branch would have imposed it on downstream users, and cabal check says so. Roughly fifty warnings across the four source files. Most were mechanical: unused imports and do-binds, shadowed names, missing local signatures. Three were not: The record update at connectTLS was incomplete because TLSSettings is a sum type and settingDisableCertificateValidation only exists on TLSSettingsSimple. Rebuilding just that constructor keeps it working on crypton-connection 0.3, which lacks the settingClientSupported field a positional call would need. Producer and Consumer are deprecated conduit synonyms, five of them in exported signatures. They expand to `forall i. ConduitT i o m ()` and `forall o. ConduitT i o m r`, not to ConduitT () o m () and ConduitT i Void m r -- those are Source and Sink. Rewriting them with the type variables left free keeps the exported types identical; pinning them to ()/Void would have narrowed the API and broken the internal fusion in mlsd and stor. MonadResource is in five exported signatures but is re-exported by Conduit, so the direct resourcet import counted as unused and -Wunused-packages then rejected the dependency. Taking the name from resourcet explicitly and hiding it from the Conduit import keeps the dependency and its version bound honest. transformers comes out of ftp-client and containers out of example; neither was imported anywhere. Deriving Typeable is dropped -- a no-op since GHC 7.10 that only GHC 9.12 warns about -- and acct, pbsz, prot, ccc and auth are now exported rather than sitting unused, matching every other command wrapper. --- example/app/Main.hs | 8 +- example/example.cabal | 32 +++++- example/package.yaml | 101 ++++++++++++++++-- example/test/Spec.hs | 2 + ftp-client-conduit/ftp-client-conduit.cabal | 29 ++++- ftp-client-conduit/package.yaml | 91 +++++++++++++++- .../src/Network/FTP/Client/Conduit.hs | 87 +++++++++------ ftp-client-conduit/test/Spec.hs | 4 + ftp-client/ftp-client.cabal | 29 ++++- ftp-client/package.yaml | 94 +++++++++++++++- ftp-client/src/Network/FTP/Client.hs | 88 ++++++++------- ftp-client/test/test.hs | 9 +- stack-base.yaml | 8 ++ 13 files changed, 471 insertions(+), 111 deletions(-) diff --git a/example/app/Main.hs b/example/app/Main.hs index 5d90df4..ae962e0 100644 --- a/example/app/Main.hs +++ b/example/app/Main.hs @@ -1,15 +1,13 @@ -module Main where +module Main (main) where import Network.FTP.Client import qualified Network.FTP.Client.Conduit as FC import Conduit -import Data.ByteString.Char8 (ByteString) import qualified Data.ByteString.Char8 as C -import Data.Monoid ((<>)) main :: IO () -main = withFTPS "hostname.com" 21 $ \h welcome -> do - login h "username" "password" +main = withFTPS "hostname.com" 21 $ \h _welcome -> do + _ <- login h "username" "password" runConduitRes $ FC.mlsd h "." .| mapC (C.pack . (<> "\n") . show) diff --git a/example/example.cabal b/example/example.cabal index 809ad46..ca64685 100644 --- a/example/example.cabal +++ b/example/example.cabal @@ -19,21 +19,36 @@ source-repository head type: git location: https://github.com/flipstone/ftp-client +flag ci + description: More strict ghc options used for development and ci, not intended for end-use. + manual: True + default: False + executable example-exe main-is: Main.hs other-modules: Paths_example hs-source-dirs: app - ghc-options: -threaded -rtsopts -with-rtsopts=-N build-depends: base , bytestring , conduit - , containers , ftp-client , ftp-client-conduit default-language: Haskell2010 + if flag(ci) + ghc-options: -O2 -threaded -rtsopts -with-rtsopts=-N -Wall -Werror -Wcompat -Widentities -Winvalid-haddock -Wmissing-local-signatures -Wmissing-export-lists -Wpartial-fields -Wmissed-specialisations -Wno-implicit-prelude -Wno-safe -Wno-unsafe -Wunused-packages + if impl (ghc <= 9.3) + ghc-options: -Wno-unticked-promoted-constructors + if impl (ghc >= 9.4) + ghc-options: -Wimplicit-lift -Woperator-whitespace -Wredundant-bang-patterns -Wredundant-strictness-flags + if impl (ghc >= 9.8) + ghc-options: -Wincomplete-export-warnings -Wmissing-poly-kind-signatures -Wterm-variable-capture + if impl (ghc >= 9.10) + ghc-options: -Wdefaulted-exception-context + else + ghc-options: -threaded -rtsopts -with-rtsopts=-N test-suite example-test type: exitcode-stdio-1.0 @@ -42,7 +57,18 @@ test-suite example-test Paths_example hs-source-dirs: test - ghc-options: -threaded -rtsopts -with-rtsopts=-N build-depends: base default-language: Haskell2010 + if flag(ci) + ghc-options: -threaded -rtsopts -with-rtsopts=-N -Wall -Werror -Wcompat -Widentities -Winvalid-haddock -Wmissing-local-signatures -Wmissing-export-lists -Wpartial-fields -Wmissed-specialisations -Wno-implicit-prelude -Wno-safe -Wno-unsafe + if impl (ghc <= 9.3) + ghc-options: -Wno-unticked-promoted-constructors + if impl (ghc >= 9.4) + ghc-options: -Wimplicit-lift -Woperator-whitespace -Wredundant-bang-patterns -Wredundant-strictness-flags + if impl (ghc >= 9.8) + ghc-options: -Wincomplete-export-warnings -Wmissing-poly-kind-signatures -Wterm-variable-capture + if impl (ghc >= 9.10) + ghc-options: -Wdefaulted-exception-context + else + ghc-options: -threaded -rtsopts -with-rtsopts=-N diff --git a/example/package.yaml b/example/package.yaml index 4bdc1b1..1fa2bc9 100644 --- a/example/package.yaml +++ b/example/package.yaml @@ -10,31 +10,114 @@ category: Web git: https://github.com/flipstone/ftp-client +flags: + ci: + description: More strict ghc options used for development and ci, not intended for end-use. + manual: true + default: false + executables: example-exe: main: Main.hs source-dirs: app language: Haskell2010 - ghc-options: - - -threaded - - -rtsopts - - -with-rtsopts=-N dependencies: - base - ftp-client - ftp-client-conduit - conduit - bytestring - - containers + when: + - condition: flag(ci) + then: + ghc-options: + - -O2 + - -threaded + - -rtsopts + - -with-rtsopts=-N + - -Wall + - -Werror + - -Wcompat + - -Widentities + - -Winvalid-haddock + - -Wmissing-local-signatures + - -Wmissing-export-lists + - -Wpartial-fields + - -Wmissed-specialisations + - -Wno-implicit-prelude + - -Wno-safe + - -Wno-unsafe + - -Wunused-packages + when: + - condition: impl (ghc <= 9.3) + ghc-options: + - -Wno-unticked-promoted-constructors + - condition: impl (ghc >= 9.4) + ghc-options: + - -Wimplicit-lift + - -Woperator-whitespace + - -Wredundant-bang-patterns + - -Wredundant-strictness-flags + - condition: impl (ghc >= 9.8) + ghc-options: + - -Wincomplete-export-warnings + - -Wmissing-poly-kind-signatures + - -Wterm-variable-capture + - condition: impl (ghc >= 9.10) + ghc-options: + - -Wdefaulted-exception-context + else: + ghc-options: + - -threaded + - -rtsopts + - -with-rtsopts=-N tests: example-test: main: Spec.hs source-dirs: test language: Haskell2010 - ghc-options: - - -threaded - - -rtsopts - - -with-rtsopts=-N dependencies: - base + when: + - condition: flag(ci) + then: + ghc-options: + - -threaded + - -rtsopts + - -with-rtsopts=-N + - -Wall + - -Werror + - -Wcompat + - -Widentities + - -Winvalid-haddock + - -Wmissing-local-signatures + - -Wmissing-export-lists + - -Wpartial-fields + - -Wmissed-specialisations + - -Wno-implicit-prelude + - -Wno-safe + - -Wno-unsafe + when: + - condition: impl (ghc <= 9.3) + ghc-options: + - -Wno-unticked-promoted-constructors + - condition: impl (ghc >= 9.4) + ghc-options: + - -Wimplicit-lift + - -Woperator-whitespace + - -Wredundant-bang-patterns + - -Wredundant-strictness-flags + - condition: impl (ghc >= 9.8) + ghc-options: + - -Wincomplete-export-warnings + - -Wmissing-poly-kind-signatures + - -Wterm-variable-capture + - condition: impl (ghc >= 9.10) + ghc-options: + - -Wdefaulted-exception-context + else: + ghc-options: + - -threaded + - -rtsopts + - -with-rtsopts=-N diff --git a/example/test/Spec.hs b/example/test/Spec.hs index cd4753f..356ebe8 100644 --- a/example/test/Spec.hs +++ b/example/test/Spec.hs @@ -1,2 +1,4 @@ +module Main (main) where + main :: IO () main = putStrLn "Test suite not yet implemented" diff --git a/ftp-client-conduit/ftp-client-conduit.cabal b/ftp-client-conduit/ftp-client-conduit.cabal index dba243a..e08f342 100644 --- a/ftp-client-conduit/ftp-client-conduit.cabal +++ b/ftp-client-conduit/ftp-client-conduit.cabal @@ -30,6 +30,11 @@ source-repository head type: git location: https://github.com/flipstone/ftp-client +flag ci + description: More strict ghc options used for development and ci, not intended for end-use. + manual: True + default: False + library exposed-modules: Network.FTP.Client.Conduit @@ -38,7 +43,6 @@ library hs-source-dirs: src default-extensions: - RankNTypes OverloadedStrings build-depends: base >=4.16 && <5 @@ -49,6 +53,16 @@ library , ftp-client ==0.5.* , resourcet >=1.2 && <1.4 default-language: Haskell2010 + if flag(ci) + ghc-options: -O2 -Wall -Werror -Wcompat -Widentities -Winvalid-haddock -Wmissing-local-signatures -Wmissing-export-lists -Wpartial-fields -Wmissed-specialisations -Wno-implicit-prelude -Wno-safe -Wno-unsafe -Wunused-packages + if impl (ghc <= 9.3) + ghc-options: -Wno-unticked-promoted-constructors + if impl (ghc >= 9.4) + ghc-options: -Wimplicit-lift -Woperator-whitespace -Wredundant-bang-patterns -Wredundant-strictness-flags + if impl (ghc >= 9.8) + ghc-options: -Wincomplete-export-warnings -Wmissing-poly-kind-signatures -Wterm-variable-capture + if impl (ghc >= 9.10) + ghc-options: -Wdefaulted-exception-context test-suite ftp-conduit-test type: exitcode-stdio-1.0 @@ -57,8 +71,19 @@ test-suite ftp-conduit-test Paths_ftp_client_conduit hs-source-dirs: test - ghc-options: -threaded -rtsopts -with-rtsopts=-N build-depends: base >=4.16 && <5 , ftp-client-conduit default-language: Haskell2010 + if flag(ci) + ghc-options: -threaded -rtsopts -with-rtsopts=-N -Wall -Werror -Wcompat -Widentities -Winvalid-haddock -Wmissing-local-signatures -Wmissing-export-lists -Wpartial-fields -Wmissed-specialisations -Wno-implicit-prelude -Wno-safe -Wno-unsafe + if impl (ghc <= 9.3) + ghc-options: -Wno-unticked-promoted-constructors + if impl (ghc >= 9.4) + ghc-options: -Wimplicit-lift -Woperator-whitespace -Wredundant-bang-patterns -Wredundant-strictness-flags + if impl (ghc >= 9.8) + ghc-options: -Wincomplete-export-warnings -Wmissing-poly-kind-signatures -Wterm-variable-capture + if impl (ghc >= 9.10) + ghc-options: -Wdefaulted-exception-context + else + ghc-options: -threaded -rtsopts -with-rtsopts=-N diff --git a/ftp-client-conduit/package.yaml b/ftp-client-conduit/package.yaml index df43bbb..7ac4a38 100644 --- a/ftp-client-conduit/package.yaml +++ b/ftp-client-conduit/package.yaml @@ -23,13 +23,18 @@ extra-source-files: git: https://github.com/flipstone/ftp-client +flags: + ci: + description: More strict ghc options used for development and ci, not intended for end-use. + manual: true + default: false + library: source-dirs: src exposed-modules: - Network.FTP.Client.Conduit language: Haskell2010 default-extensions: - - RankNTypes - OverloadedStrings dependencies: - base >= 4.16 && < 5 @@ -39,16 +44,92 @@ library: - resourcet >= 1.2 && < 1.4 - crypton-connection >= 0.3 && < 0.5 - exceptions + when: + - condition: flag(ci) + ghc-options: + - -O2 + - -Wall + - -Werror + - -Wcompat + - -Widentities + - -Winvalid-haddock + - -Wmissing-local-signatures + - -Wmissing-export-lists + - -Wpartial-fields + - -Wmissed-specialisations + - -Wno-implicit-prelude + - -Wno-safe + - -Wno-unsafe + - -Wunused-packages + when: + # Previously the guidance was to always use ticked constructors for type + # constructors, but that guidance has been reversed, so we turn the + # warning off in older compilers. + - condition: impl (ghc <= 9.3) + ghc-options: + - -Wno-unticked-promoted-constructors + - condition: impl (ghc >= 9.4) + ghc-options: + - -Wimplicit-lift + - -Woperator-whitespace + - -Wredundant-bang-patterns + - -Wredundant-strictness-flags + - condition: impl (ghc >= 9.8) + ghc-options: + - -Wincomplete-export-warnings + - -Wmissing-poly-kind-signatures + - -Wterm-variable-capture + - condition: impl (ghc >= 9.10) + ghc-options: + - -Wdefaulted-exception-context tests: ftp-conduit-test: main: Spec.hs source-dirs: test language: Haskell2010 - ghc-options: - - -threaded - - -rtsopts - - -with-rtsopts=-N dependencies: - base >= 4.16 && < 5 - ftp-client-conduit + when: + - condition: flag(ci) + then: + ghc-options: + - -threaded + - -rtsopts + - -with-rtsopts=-N + - -Wall + - -Werror + - -Wcompat + - -Widentities + - -Winvalid-haddock + - -Wmissing-local-signatures + - -Wmissing-export-lists + - -Wpartial-fields + - -Wmissed-specialisations + - -Wno-implicit-prelude + - -Wno-safe + - -Wno-unsafe + when: + - condition: impl (ghc <= 9.3) + ghc-options: + - -Wno-unticked-promoted-constructors + - condition: impl (ghc >= 9.4) + ghc-options: + - -Wimplicit-lift + - -Woperator-whitespace + - -Wredundant-bang-patterns + - -Wredundant-strictness-flags + - condition: impl (ghc >= 9.8) + ghc-options: + - -Wincomplete-export-warnings + - -Wmissing-poly-kind-signatures + - -Wterm-variable-capture + - condition: impl (ghc >= 9.10) + ghc-options: + - -Wdefaulted-exception-context + else: + ghc-options: + - -threaded + - -rtsopts + - -with-rtsopts=-N diff --git a/ftp-client-conduit/src/Network/FTP/Client/Conduit.hs b/ftp-client-conduit/src/Network/FTP/Client/Conduit.hs index 39955fd..8b9dc17 100644 --- a/ftp-client-conduit/src/Network/FTP/Client/Conduit.hs +++ b/ftp-client-conduit/src/Network/FTP/Client/Conduit.hs @@ -1,3 +1,5 @@ +{-# LANGUAGE ScopedTypeVariables #-} + {-| Module : Network.FTP.Client Description : Transfer files over FTP and FTPS with Conduit @@ -14,23 +16,21 @@ module Network.FTP.Client.Conduit ( mlsd ) where -import Conduit -import Control.Monad.IO.Class +-- MonadResource appears in this module's exported signatures, so it is taken +-- from resourcet directly rather than through Conduit's re-export. That keeps +-- the resourcet dependency honest instead of implicit. +import Conduit hiding (MonadResource) +import Control.Monad.Trans.Resource (MonadResource) import Data.ByteString.Lazy.Internal (defaultChunkSize) import System.IO import Network.FTP.Client - ( sendCommand - , sendCommandS - , sendAll - , sendAllS + ( sendCommandS , FTPCommand(..) , RTypeCode(..) - , getLineResp , createSendDataCommand , createTLSSendDataCommand , PortActivity(..) , getResponse - , getResponseS , sIOHandleImpl , tlsHandleImpl , Security(..) @@ -40,9 +40,6 @@ import Network.FTP.Client import qualified Network.FTP.Client as FTP import qualified Data.ByteString as B import Data.ByteString (ByteString) -import Control.Monad.Trans.Resource -import Data.Monoid ((<>)) -import System.IO.Error import Network.Connection import qualified Control.Monad.Catch as M @@ -50,17 +47,21 @@ debugging :: Bool debugging = False debugPrint :: (Show a, MonadIO m) => a -> m () -debugPrint s = debugPrint' s debugging - where - debugPrint' _ False = return () - debugPrint' s True = liftIO $ print s +debugPrint s = + if debugging + then liftIO $ print s + else return () debugResponse :: (Show a, MonadIO m) => a -> m () debugResponse s = debugPrint $ "Recieved: " <> (show s) -getAllLineRespC :: MonadIO m => FTP.Handle -> Producer m ByteString -getAllLineRespC h = loop - where +getAllLineRespC + :: forall i m + . MonadIO m + => FTP.Handle + -> ConduitT i ByteString m () +getAllLineRespC h = + let loop :: ConduitT i ByteString m () loop = do line <- liftIO $ FTP.getLineResp h `M.catchIOError` const (return "") @@ -69,10 +70,15 @@ getAllLineRespC h = loop else do yield line loop + in loop -sendAllLineC :: MonadIO m => FTP.Handle -> Consumer ByteString m () -sendAllLineC h = loop - where +sendAllLineC + :: forall o m + . MonadIO m + => FTP.Handle + -> ConduitT ByteString o m () +sendAllLineC h = + let loop :: ConduitT ByteString o m () loop = do mx <- await case mx of @@ -80,6 +86,7 @@ sendAllLineC h = loop Just x -> do liftIO $ FTP.sendLine h x loop + in loop sourceDataCommandSecurity :: MonadResource m @@ -103,7 +110,7 @@ sourceDataCommand -> (FTP.Handle -> ConduitM i o m r) -> ConduitM i o m r sourceDataCommand ch pa code cmd f = do - sendCommandS ch $ RType code + _ <- sendCommandS ch $ RType code x <- bracketP (createSendDataCommand ch pa cmd) (liftIO . hClose) @@ -121,7 +128,7 @@ sourceTLSDataCommand -> (FTP.Handle -> ConduitM i o m r) -> ConduitM i o m r sourceTLSDataCommand ch pa code cmd f = do - sendCommandS ch $ RType code + _ <- sendCommandS ch $ RType code x <- bracketP (createTLSSendDataCommand ch pa cmd) (liftIO . connectionClose) @@ -130,9 +137,13 @@ sourceTLSDataCommand ch pa code cmd f = do debugResponse resp return x -sourceFTPHandle :: MonadIO m => FTP.Handle -> Producer m ByteString -sourceFTPHandle h = loop - where +sourceFTPHandle + :: forall i m + . MonadIO m + => FTP.Handle + -> ConduitT i ByteString m () +sourceFTPHandle h = + let loop :: ConduitT i ByteString m () loop = do bs <- liftIO $ FTP.recv h defaultChunkSize `M.catchIOError` const (return "") @@ -141,10 +152,15 @@ sourceFTPHandle h = loop else do yield bs loop + in loop -sinkFTPHandle :: MonadIO m => FTP.Handle -> Consumer ByteString m () -sinkFTPHandle h = loop - where +sinkFTPHandle + :: forall o m + . MonadIO m + => FTP.Handle + -> ConduitT ByteString o m () +sinkFTPHandle h = + let loop :: ConduitT ByteString o m () loop = do mbs <- await case mbs of @@ -152,24 +168,25 @@ sinkFTPHandle h = loop Just bs -> do liftIO $ FTP.send h bs loop + in loop sendType :: MonadResource m => RTypeCode -> FTP.Handle - -> Consumer ByteString m () + -> ConduitT ByteString o m () sendType TA h = sendAllLineC h sendType TI h = sinkFTPHandle h -nlst :: MonadResource m => FTP.Handle -> [String] -> Producer m ByteString +nlst :: MonadResource m => FTP.Handle -> [String] -> ConduitT i ByteString m () nlst ch args = sourceDataCommandSecurity ch Passive TA (Nlst args) getAllLineRespC -retr :: MonadResource m => FTP.Handle -> String -> Producer m ByteString +retr :: MonadResource m => FTP.Handle -> String -> ConduitT i ByteString m () retr ch path = sourceDataCommandSecurity ch Passive TI (Retr path) sourceFTPHandle -list :: MonadResource m => FTP.Handle -> [String] -> Producer m ByteString +list :: MonadResource m => FTP.Handle -> [String] -> ConduitT i ByteString m () list ch args = sourceDataCommandSecurity ch Passive TA (List args) getAllLineRespC @@ -178,7 +195,7 @@ stor => FTP.Handle -> String -> RTypeCode - -> Consumer ByteString m () + -> ConduitT ByteString o m () stor ch loc rtype = sourceDataCommandSecurity ch Passive rtype (Stor loc) $ sendType rtype @@ -186,7 +203,7 @@ mlsd :: MonadResource m => FTP.Handle -> String - -> Producer m FTP.MlsxResponse + -> ConduitT i FTP.MlsxResponse m () mlsd ch dir = sourceDataCommandSecurity ch Passive TA (Mlsd dir) getAllLineRespC .| mapC parseMlsxLine diff --git a/ftp-client-conduit/test/Spec.hs b/ftp-client-conduit/test/Spec.hs index cd4753f..9531f64 100644 --- a/ftp-client-conduit/test/Spec.hs +++ b/ftp-client-conduit/test/Spec.hs @@ -1,2 +1,6 @@ +module Main (main) where + +import Network.FTP.Client.Conduit () + main :: IO () main = putStrLn "Test suite not yet implemented" diff --git a/ftp-client/ftp-client.cabal b/ftp-client/ftp-client.cabal index b658910..df8a1cf 100644 --- a/ftp-client/ftp-client.cabal +++ b/ftp-client/ftp-client.cabal @@ -30,6 +30,11 @@ source-repository head type: git location: https://github.com/flipstone/ftp-client +flag ci + description: More strict ghc options used for development and ci, not intended for end-use. + manual: True + default: False + library exposed-modules: Network.FTP.Client @@ -48,8 +53,17 @@ library , data-default-class >=0.1.2.0 && <0.3 , exceptions >=0.10.3 && <0.11 , network >=2.6.3.6 && <3.3 - , transformers >=0.5.6.2 && <0.7 default-language: Haskell2010 + if flag(ci) + ghc-options: -O2 -Wall -Werror -Wcompat -Widentities -Winvalid-haddock -Wmissing-local-signatures -Wmissing-export-lists -Wpartial-fields -Wmissed-specialisations -Wno-implicit-prelude -Wno-safe -Wno-unsafe -Wunused-packages + if impl (ghc <= 9.3) + ghc-options: -Wno-unticked-promoted-constructors + if impl (ghc >= 9.4) + ghc-options: -Wimplicit-lift -Woperator-whitespace -Wredundant-bang-patterns -Wredundant-strictness-flags + if impl (ghc >= 9.8) + ghc-options: -Wincomplete-export-warnings -Wmissing-poly-kind-signatures -Wterm-variable-capture + if impl (ghc >= 9.10) + ghc-options: -Wdefaulted-exception-context test-suite ftp-client-test type: exitcode-stdio-1.0 @@ -58,10 +72,21 @@ test-suite ftp-client-test Paths_ftp_client hs-source-dirs: test - ghc-options: -threaded -rtsopts -with-rtsopts=-N build-depends: base >=4.16 && <5 , bytestring >=0.10.8.2 && <0.13 , ftp-client , hspec >=2.7 default-language: Haskell2010 + if flag(ci) + ghc-options: -threaded -rtsopts -with-rtsopts=-N -Wall -Werror -Wcompat -Widentities -Winvalid-haddock -Wmissing-local-signatures -Wmissing-export-lists -Wpartial-fields -Wmissed-specialisations -Wno-implicit-prelude -Wno-safe -Wno-unsafe + if impl (ghc <= 9.3) + ghc-options: -Wno-unticked-promoted-constructors + if impl (ghc >= 9.4) + ghc-options: -Wimplicit-lift -Woperator-whitespace -Wredundant-bang-patterns -Wredundant-strictness-flags + if impl (ghc >= 9.8) + ghc-options: -Wincomplete-export-warnings -Wmissing-poly-kind-signatures -Wterm-variable-capture + if impl (ghc >= 9.10) + ghc-options: -Wdefaulted-exception-context + else + ghc-options: -threaded -rtsopts -with-rtsopts=-N diff --git a/ftp-client/package.yaml b/ftp-client/package.yaml index 72493b2..780339b 100644 --- a/ftp-client/package.yaml +++ b/ftp-client/package.yaml @@ -23,6 +23,12 @@ extra-source-files: git: https://github.com/flipstone/ftp-client +flags: + ci: + description: More strict ghc options used for development and ci, not intended for end-use. + manual: true + default: false + library: source-dirs: src exposed-modules: @@ -36,22 +42,100 @@ library: - network >= 2.6.3.6 && < 3.3 - attoparsec >= 0.10 && < 0.15 - crypton-connection >= 0.3 && < 0.5 - - transformers >= 0.5.6.2 && < 0.7 - exceptions >= 0.10.3 && < 0.11 - containers >= 0.5.11.0 && < 0.8 - data-default-class >= 0.1.2.0 && < 0.3 + when: + - condition: flag(ci) + ghc-options: + - -O2 + - -Wall + - -Werror + - -Wcompat + - -Widentities + - -Winvalid-haddock + - -Wmissing-local-signatures + - -Wmissing-export-lists + - -Wpartial-fields + - -Wmissed-specialisations + - -Wno-implicit-prelude + - -Wno-safe + - -Wno-unsafe + - -Wunused-packages + when: + # Previously the guidance was to always use ticked constructors for type + # constructors, but that guidance has been reversed, so we turn the + # warning off in older compilers. + - condition: impl (ghc <= 9.3) + ghc-options: + - -Wno-unticked-promoted-constructors + - condition: impl (ghc >= 9.4) + ghc-options: + - -Wimplicit-lift + - -Woperator-whitespace + - -Wredundant-bang-patterns + - -Wredundant-strictness-flags + - condition: impl (ghc >= 9.8) + ghc-options: + - -Wincomplete-export-warnings + - -Wmissing-poly-kind-signatures + - -Wterm-variable-capture + - condition: impl (ghc >= 9.10) + ghc-options: + - -Wdefaulted-exception-context tests: ftp-client-test: main: test.hs source-dirs: test language: Haskell2010 - ghc-options: - - -threaded - - -rtsopts - - -with-rtsopts=-N dependencies: - base >= 4.16 && < 5 - bytestring >= 0.10.8.2 && < 0.13 - hspec >= 2.7 - ftp-client + when: + - condition: flag(ci) + then: + ghc-options: + - -threaded + - -rtsopts + - -with-rtsopts=-N + - -Wall + - -Werror + - -Wcompat + - -Widentities + - -Winvalid-haddock + - -Wmissing-local-signatures + - -Wmissing-export-lists + - -Wpartial-fields + - -Wmissed-specialisations + - -Wno-implicit-prelude + - -Wno-safe + - -Wno-unsafe + when: + # Previously the guidance was to always use ticked constructors for type + # constructors, but that guidance has been reversed, so we turn the + # warning off in older compilers. + - condition: impl (ghc <= 9.3) + ghc-options: + - -Wno-unticked-promoted-constructors + - condition: impl (ghc >= 9.4) + ghc-options: + - -Wimplicit-lift + - -Woperator-whitespace + - -Wredundant-bang-patterns + - -Wredundant-strictness-flags + - condition: impl (ghc >= 9.8) + ghc-options: + - -Wincomplete-export-warnings + - -Wmissing-poly-kind-signatures + - -Wterm-variable-capture + - condition: impl (ghc >= 9.10) + ghc-options: + - -Wdefaulted-exception-context + else: + ghc-options: + - -threaded + - -rtsopts + - -with-rtsopts=-N diff --git a/ftp-client/src/Network/FTP/Client.hs b/ftp-client/src/Network/FTP/Client.hs index a9d894a..e1e33a9 100644 --- a/ftp-client/src/Network/FTP/Client.hs +++ b/ftp-client/src/Network/FTP/Client.hs @@ -16,6 +16,7 @@ module Network.FTP.Client ( dele, cwd, size, + acct, mkd, rmd, pwd, @@ -38,6 +39,11 @@ module Network.FTP.Client ( ProtType(..), Security(..), Handle(..), + -- * TLS Commands + pbsz, + prot, + ccc, + auth, -- * Exceptions FTPException(..), -- * System Handle Creation @@ -66,11 +72,10 @@ import Data.Default.Class (def) import qualified Data.ByteString.Char8 as C import qualified Data.ByteString as B import Data.ByteString (ByteString) -import Data.List +import Data.List (intercalate) import Data.Attoparsec.ByteString.Char8 import qualified Network.Socket as S import qualified System.IO as SIO -import Data.Monoid ((<>)) import Control.Exception import Control.Monad.Catch (MonadCatch, MonadMask) import qualified Control.Monad.Catch as M @@ -79,12 +84,9 @@ import Control.Monad.IO.Class import Data.Bits import Network.Connection import Data.ByteString.Lazy.Internal (defaultChunkSize) -import Data.Functor ((<$>)) -import Control.Applicative ((<*>)) import Data.Map.Strict (Map) import qualified Data.Map.Strict as Map import Control.Arrow -import Data.Typeable import System.IO.Error (isEOFError) debugging :: Bool @@ -139,7 +141,7 @@ data FTPException | UnsuccessfulException FTPResponse | BogusResponseFormatException FTPResponse | BadProtocolResponseException ByteString - deriving (Show, Typeable) + deriving (Show) instance Exception FTPException @@ -206,7 +208,7 @@ formatPort ha pn = serializeCommand :: FTPCommand -> String serializeCommand (User user) = "USER " <> user serializeCommand (Pass pass) = "PASS " <> pass -serializeCommand (Acct acct) = "ACCT " <> acct +serializeCommand (Acct account) = "ACCT " <> account serializeCommand (RType rt) = "TYPE " <> serialzeRTypeCode rt serializeCommand (Retr file) = "RETR " <> file serializeCommand (Nlst []) = "NLST" @@ -267,10 +269,10 @@ getResponse h = do Just ('-', _) -> MultiLine <$> loopMultiLine h code [line] _ -> return $ SingleLine line let codeDroppedMessage = case message of - SingleLine message -> SingleLine $ C.drop 4 message + SingleLine singleMessage -> SingleLine $ C.drop 4 singleMessage MultiLine [] -> MultiLine [] - MultiLine (message:messages) -> - MultiLine $ C.drop 4 message : messages + MultiLine (firstMessage:messages) -> + MultiLine $ C.drop 4 firstMessage : messages let response = FTPResponse (responseStatus code) (read $ C.unpack code) @@ -286,7 +288,7 @@ loopMultiLine -> ByteString -> [ByteString] -> m [ByteString] -loopMultiLine h code lines = do +loopMultiLine h code priorLines = do mNextLine <- liftIO $ getLineRespMaybe h case mNextLine of -- The server hung up before sending the terminating line. Stop rather @@ -300,13 +302,13 @@ loopMultiLine h code lines = do -- lines of a multiline reply hold arbitrary text, blank lines included, -- so a blank line has to be kept and the loop has to continue past it. Nothing -> liftIO $ throwIO $ BadProtocolResponseException - $ C.intercalate "\n" lines + $ C.intercalate "\n" priorLines Just nextLine -> do -- RFC 959 (https://datatracker.ietf.org/doc/html/rfc959#page-36) ends a -- multiline reply with the code followed by a space, and continues it -- with the code followed by a hyphen. The bare code is accepted too, -- for servers that omit the trailing space on an empty final line. - let newLines = lines <> [C.dropWhile (== ' ') nextLine] + let newLines = priorLines <> [C.dropWhile (== ' ') nextLine] isLastLine = nextLine == code || C.isPrefixOf (code <> " ") nextLine @@ -385,9 +387,9 @@ withSocketPassive host portNum f = do (createSocket (Just host) portNum hints) (liftIO . S.close . fst) (\(sock, addr) -> do - debugPrint "Connecting" + debugPrint ("Connecting" :: String) liftIO $ S.connect sock (S.addrAddress addr) - debugPrint "Connected" + debugPrint ("Connected" :: String) f sock ) @@ -401,10 +403,10 @@ withSocketActive f = do (createSocket Nothing 0 hints) (liftIO . S.close . fst) (\(sock, addr) -> do - debugPrint "Binding" + debugPrint ("Binding" :: String) liftIO $ S.bind sock (S.addrAddress addr) liftIO $ S.listen sock 1 - debugPrint "Listening" + debugPrint ("Listening" :: String) f sock ) @@ -471,9 +473,9 @@ withDataSocketActive -> m a withDataSocketActive h f = withSocketActive $ \socket -> do (sPort, sHost) <- liftIO $ do - (S.SockAddrInet p h) <- S.getSocketName socket - return (p,h) - port h sHost sPort + (S.SockAddrInet p hostAddr) <- S.getSocketName socket + return (p,hostAddr) + _ <- port h sHost sPort f socket -- | Open a socket that can be used for data transfers @@ -527,7 +529,7 @@ withDataCommand -> (Handle -> m a) -> m a withDataCommand ch pa code cmd f = do - sendCommandS ch $ RType code + _ <- sendCommandS ch $ RType code x <- M.bracket (createSendDataCommand ch pa cmd) (liftIO . SIO.hClose) @@ -538,32 +540,35 @@ withDataCommand ch pa code cmd f = do -- | Recieve data and interpret it linewise getAllLineResp :: (MonadIO m, MonadCatch m) => Handle -> m ByteString -getAllLineResp h = getAllLineResp' h [] - where - getAllLineResp' h ret = ( do +getAllLineResp h = + let collect :: (MonadIO n, MonadCatch n) => [ByteString] -> n ByteString + collect ret = ( do line <- liftIO $ getLineResp h - getAllLineResp' h (ret <> [line])) + collect (ret <> [line])) `M.catchIOError` (\_ -> return $ C.intercalate "\n" ret) + in collect [] -- | Recieve all data and return it as a 'Data.ByteString.ByteString' recvAll :: (MonadIO m, MonadCatch m) => Handle -> m ByteString -recvAll h = recvAll' "" - where - recvAll' bs = ( do +recvAll h = + let collect :: (MonadIO n, MonadCatch n) => ByteString -> n ByteString + collect bs = ( do chunk <- liftIO $ recv h defaultChunkSize if C.null chunk then return bs - else recvAll' $ bs <> chunk + else collect $ bs <> chunk ) `M.catchIOError` (\_ -> return bs) + in collect "" -- TLS connection connectTLS :: MonadIO m => SIO.Handle -> String -> Int -> m Connection connectTLS h host portNum = do context <- liftIO initConnectionContext - let tlsSettings = def - { settingDisableCertificateValidation = True - } + let tlsSettings = case def of + simpleSettings@TLSSettingsSimple{} -> + simpleSettings { settingDisableCertificateValidation = True } + otherSettings -> otherSettings connectionParams = ConnectionParams { connectionHostname = host , connectionPort = toEnum . fromEnum $ portNum @@ -581,7 +586,7 @@ createTLSConnection host portNum = do h <- createSIOHandle host portNum let insecureH = sIOHandleImpl h resp <- getResponse insecureH - sendCommand insecureH Auth + _ <- sendCommand insecureH Auth conn <- connectTLS h host portNum return (resp, conn) @@ -633,7 +638,7 @@ createTLSSendDataCommand -> FTPCommand -> m Connection createTLSSendDataCommand ch pa cmd = do - sendAllS ch [Pbsz 0, Prot P] + _ <- sendAllS ch [Pbsz 0, Prot P] withDataSocket pa ch $ \socket -> do resp <- sendCommand ch cmd ensureSucessfulData ch resp @@ -655,7 +660,7 @@ withTLSDataCommand -> (Handle -> m a) -> m a withTLSDataCommand ch pa code cmd f = do - sendCommandS ch $ RType code + _ <- sendCommandS ch $ RType code x <- M.bracket (createTLSSendDataCommand ch pa cmd) (liftIO . connectionClose) @@ -681,7 +686,7 @@ ensureCode resp code = parse227 :: Parser (String, Int) parse227 = do - skipWhile (/= '(') *> char '(' + _ <- skipWhile (/= '(') *> char '(' [h1,h2,h3,h4,p1,p2] <- many1 digit `sepBy` char ',' let host = intercalate "." [h1,h2,h3,h4] highBits = read p1 @@ -691,7 +696,7 @@ parse227 = do parse257 :: Parser String parse257 = do - char '"' + _ <- char '"' C.unpack <$> takeTill (== '"') -- Control commands @@ -837,15 +842,16 @@ parseMlsxLine line = in MlsxResponse (C.unpack filename) facts getMlsxResponse :: (MonadIO m, MonadCatch m) => Handle -> m [MlsxResponse] -getMlsxResponse h = getMlsxResponse' h [] - where - getMlsxResponse' h ret = ( do +getMlsxResponse h = + let collect :: (MonadIO n, MonadCatch n) => [MlsxResponse] -> n [MlsxResponse] + collect ret = ( do line <- liftIO $ getLineResp h - getMlsxResponse' h $ + collect $ if C.null line then ret else parseMlsxLine line : ret ) `M.catchIOError` (\_ -> return ret) + in collect [] mlsd :: (MonadIO m, MonadMask m) => Handle -> String -> m [MlsxResponse] mlsd h path = withDataCommandSecurity h Passive TA (Mlsd path) getMlsxResponse diff --git a/ftp-client/test/test.hs b/ftp-client/test/test.hs index c4738b9..aac5058 100644 --- a/ftp-client/test/test.hs +++ b/ftp-client/test/test.hs @@ -1,16 +1,17 @@ +module Main (main) where + import Data.ByteString (ByteString) import qualified Data.ByteString.Char8 as C import Test.Hspec import Network.FTP.Client hiding (Success) import qualified Network.FTP.Client as F -import Control.Monad.IO.Class import Control.Concurrent.MVar import System.IO.Error (eofErrorType, mkIOError) data TestHandleMVars = TestHandleMVars { thmSend :: MVar [ByteString] - , thmSendLine :: MVar [ByteString] - , thmRecv :: MVar [Int] + , _thmSendLine :: MVar [ByteString] + , _thmRecv :: MVar [Int] } data TestHandle = TestHandle TestHandleMVars Handle @@ -156,7 +157,7 @@ main = hspec $ do describe "Network.FTP.Client.recvAll" $ it "doesn't hang on empty response" $ do let expected = C.pack "" - (TestHandle mvars h) <- testHandle [C.pack ""] [] Clear + (TestHandle _ h) <- testHandle [C.pack ""] [] Clear recvAll h `shouldReturn` expected isBadProtocolResponse :: FTPException -> Bool diff --git a/stack-base.yaml b/stack-base.yaml index b7fa0aa..f622551 100644 --- a/stack-base.yaml +++ b/stack-base.yaml @@ -3,4 +3,12 @@ packages: - 'ftp-client-conduit' - 'example' +flags: + ftp-client: + ci: true + ftp-client-conduit: + ci: true + example: + ci: true + pvp-bounds: both From d66410b5edbfed9f92abf7c38592e969a1caf860 Mon Sep 17 00:00:00 2001 From: Paul Burns Date: Tue, 18 Aug 2026 19:49:05 -0400 Subject: [PATCH 2/5] Add the missing upper bounds on ftp-client-conduit cabal check flagged bytestring, conduit and exceptions as having no upper bound. The bounds match what the matrix actually exercises -- conduit 1.3.5 through 1.3.6.1, bytestring 0.11.4.0 through 0.12.2.0, exceptions 0.10.4 through 0.10.12 -- rounded to the next major in each case, and the bytestring and exceptions ranges are spelled the same way ftp-client already spells them. Existing lower bounds are left alone; only the missing upper halves are added. cabal check is now clean on both published packages. --- ftp-client-conduit/ftp-client-conduit.cabal | 6 +++--- ftp-client-conduit/package.yaml | 6 +++--- 2 files changed, 6 insertions(+), 6 deletions(-) diff --git a/ftp-client-conduit/ftp-client-conduit.cabal b/ftp-client-conduit/ftp-client-conduit.cabal index e08f342..cbf8cb5 100644 --- a/ftp-client-conduit/ftp-client-conduit.cabal +++ b/ftp-client-conduit/ftp-client-conduit.cabal @@ -46,10 +46,10 @@ library OverloadedStrings build-depends: base >=4.16 && <5 - , bytestring - , conduit >=1.1 + , bytestring >=0.10.8.2 && <0.13 + , conduit >=1.1 && <1.4 , crypton-connection >=0.3 && <0.5 - , exceptions + , exceptions >=0.10.3 && <0.11 , ftp-client ==0.5.* , resourcet >=1.2 && <1.4 default-language: Haskell2010 diff --git a/ftp-client-conduit/package.yaml b/ftp-client-conduit/package.yaml index 7ac4a38..471e458 100644 --- a/ftp-client-conduit/package.yaml +++ b/ftp-client-conduit/package.yaml @@ -39,11 +39,11 @@ library: dependencies: - base >= 4.16 && < 5 - ftp-client == 0.5.* - - conduit >= 1.1 - - bytestring + - conduit >= 1.1 && < 1.4 + - bytestring >= 0.10.8.2 && < 0.13 - resourcet >= 1.2 && < 1.4 - crypton-connection >= 0.3 && < 0.5 - - exceptions + - exceptions >= 0.10.3 && < 0.11 when: - condition: flag(ci) ghc-options: From 19775cb23b01ed60d2b241bee4d30b6b170825b7 Mon Sep 17 00:00:00 2001 From: Paul Burns Date: Tue, 18 Aug 2026 19:54:39 -0400 Subject: [PATCH 3/5] Version ftp-client 0.5.3.0, ftp-client-conduit 0.5.0.7 ftp-client gains five exports -- acct, pbsz, prot, ccc and auth -- which is an additive API change, so PVP asks for a minor bump. It also loses the transformers dependency and the no-op Typeable deriving. ftp-client-conduit only gains dependency bounds; the Producer and Consumer rewrite is type-identical because the type variables are left free, so a patch bump covers it. --- ftp-client-conduit/CHANGELOG.md | 9 +++++++++ ftp-client-conduit/ftp-client-conduit.cabal | 2 +- ftp-client-conduit/package.yaml | 2 +- ftp-client/CHANGELOG.md | 11 +++++++++++ ftp-client/ftp-client.cabal | 2 +- ftp-client/package.yaml | 2 +- 6 files changed, 24 insertions(+), 4 deletions(-) diff --git a/ftp-client-conduit/CHANGELOG.md b/ftp-client-conduit/CHANGELOG.md index c63620f..073de77 100644 --- a/ftp-client-conduit/CHANGELOG.md +++ b/ftp-client-conduit/CHANGELOG.md @@ -1,5 +1,14 @@ # Changelog for ftp-client-conduit +## 0.5.0.7 + +* Add the missing upper bounds on `bytestring`, `conduit` and `exceptions`. + +* Replace conduit's deprecated `Producer` and `Consumer` synonyms with + `ConduitT` in the exported signatures of `nlst`, `retr`, `list`, `stor` and + `mlsd`. The type variables are left free, so the exported types are unchanged + -- `Producer m o` is `forall i. ConduitT i o m ()`, not `ConduitT () o m ()`. + ## 0.5.0.6 * Correct the `resourcet` bound. `>= 1.2 && < 1.3` excluded `resourcet-1.3.0`, diff --git a/ftp-client-conduit/ftp-client-conduit.cabal b/ftp-client-conduit/ftp-client-conduit.cabal index cbf8cb5..32e6ba8 100644 --- a/ftp-client-conduit/ftp-client-conduit.cabal +++ b/ftp-client-conduit/ftp-client-conduit.cabal @@ -5,7 +5,7 @@ cabal-version: 1.12 -- see: https://github.com/sol/hpack name: ftp-client-conduit -version: 0.5.0.6 +version: 0.5.0.7 synopsis: Transfer file with FTP and FTPS with Conduit description: ftp-client is a library for communicating with an FTP server. It works over both a clear channel or TLS. ftp-client-conduit uses conduit to stream files and data in constant space. category: Web diff --git a/ftp-client-conduit/package.yaml b/ftp-client-conduit/package.yaml index 471e458..0e2ca2a 100644 --- a/ftp-client-conduit/package.yaml +++ b/ftp-client-conduit/package.yaml @@ -1,5 +1,5 @@ name: ftp-client-conduit -version: 0.5.0.6 +version: 0.5.0.7 synopsis: Transfer file with FTP and FTPS with Conduit description: ftp-client is a library for communicating with an FTP server. It works over both a clear channel or TLS. ftp-client-conduit uses conduit to stream files and data in constant space. homepage: https://github.com/flipstone/ftp-client diff --git a/ftp-client/CHANGELOG.md b/ftp-client/CHANGELOG.md index 8ddf20f..53f7ef4 100644 --- a/ftp-client/CHANGELOG.md +++ b/ftp-client/CHANGELOG.md @@ -1,5 +1,16 @@ # Changelog for ftp-client +## 0.5.3.0 + +* Export `acct`, `pbsz`, `prot`, `ccc` and `auth`. These command wrappers were + defined but never exported, unlike every other command wrapper in the module. + +* Drop the `transformers` dependency. The only module it supplied, + `Control.Monad.IO.Class`, has been in `base` since 4.9. + +* Stop deriving `Typeable` for `FTPException`. It has been a no-op since GHC + 7.10 and GHC 9.12 warns about it. + ## 0.5.2.0 * Expose `createSIOHandle`, `createTLSConnection` and `connectTLS` so callers can diff --git a/ftp-client/ftp-client.cabal b/ftp-client/ftp-client.cabal index df8a1cf..c6993fc 100644 --- a/ftp-client/ftp-client.cabal +++ b/ftp-client/ftp-client.cabal @@ -5,7 +5,7 @@ cabal-version: 1.12 -- see: https://github.com/sol/hpack name: ftp-client -version: 0.5.2.0 +version: 0.5.3.0 synopsis: Transfer files with FTP and FTPS description: ftp-client is a library for communicating with an FTP server. It works over both a clear channel or TLS. category: Web diff --git a/ftp-client/package.yaml b/ftp-client/package.yaml index 780339b..70400cc 100644 --- a/ftp-client/package.yaml +++ b/ftp-client/package.yaml @@ -1,5 +1,5 @@ name: ftp-client -version: 0.5.2.0 +version: 0.5.3.0 synopsis: Transfer files with FTP and FTPS description: ftp-client is a library for communicating with an FTP server. It works over both a clear channel or TLS. homepage: https://github.com/flipstone/ftp-client From b282da19368c9886ebc24c367a5d5ec3bb17f736 Mon Sep 17 00:00:00 2001 From: Paul Burns Date: Mon, 24 Aug 2026 20:43:21 -0400 Subject: [PATCH 4/5] Raise the conduit lower bound to match the ConduitT rewrite Replacing the deprecated Producer and Consumer synonyms put ConduitT into the exported signatures, and ConduitT arrived in conduit 1.3. The bound still said >= 1.1, so cabal was free to pick a conduit that cannot compile the module. The bounds audit in the preceding commit deliberately touched only the missing upper halves and left existing lower bounds alone. This is the one lower bound that the same commit's own change invalidated, so it belongs here rather than with the audit. hpack renders >= 1.3 && < 1.4 as ==1.3.*, which is why the generated cabal file reads that way; it is the same constraint and the same spelling already used for ftp-client ==0.5.*. Reported by Copilot on flipstone/ftp-client#6. Co-Authored-By: Claude Opus 5 (1M context) --- ftp-client-conduit/CHANGELOG.md | 6 ++++++ ftp-client-conduit/ftp-client-conduit.cabal | 2 +- ftp-client-conduit/package.yaml | 2 +- 3 files changed, 8 insertions(+), 2 deletions(-) diff --git a/ftp-client-conduit/CHANGELOG.md b/ftp-client-conduit/CHANGELOG.md index 073de77..7b2df8c 100644 --- a/ftp-client-conduit/CHANGELOG.md +++ b/ftp-client-conduit/CHANGELOG.md @@ -9,6 +9,12 @@ `mlsd`. The type variables are left free, so the exported types are unchanged -- `Producer m o` is `forall i. ConduitT i o m ()`, not `ConduitT () o m ()`. +* Raise the `conduit` lower bound to `>= 1.3`. `ConduitT` arrived in conduit + 1.3, so the previous `>= 1.1` allowed dependency selections that cannot + compile the signatures above. The bounds audit only added the missing upper + halves; this is the lower half that replacing `Producer` and `Consumer` + invalidated. + ## 0.5.0.6 * Correct the `resourcet` bound. `>= 1.2 && < 1.3` excluded `resourcet-1.3.0`, diff --git a/ftp-client-conduit/ftp-client-conduit.cabal b/ftp-client-conduit/ftp-client-conduit.cabal index 32e6ba8..bf47c60 100644 --- a/ftp-client-conduit/ftp-client-conduit.cabal +++ b/ftp-client-conduit/ftp-client-conduit.cabal @@ -47,7 +47,7 @@ library build-depends: base >=4.16 && <5 , bytestring >=0.10.8.2 && <0.13 - , conduit >=1.1 && <1.4 + , conduit ==1.3.* , crypton-connection >=0.3 && <0.5 , exceptions >=0.10.3 && <0.11 , ftp-client ==0.5.* diff --git a/ftp-client-conduit/package.yaml b/ftp-client-conduit/package.yaml index 0e2ca2a..eee8671 100644 --- a/ftp-client-conduit/package.yaml +++ b/ftp-client-conduit/package.yaml @@ -39,7 +39,7 @@ library: dependencies: - base >= 4.16 && < 5 - ftp-client == 0.5.* - - conduit >= 1.1 && < 1.4 + - conduit >= 1.3 && < 1.4 - bytestring >= 0.10.8.2 && < 0.13 - resourcet >= 1.2 && < 1.4 - crypton-connection >= 0.3 && < 0.5 From fc0af21bb658dd03ecbc7639ab1483fcb8001c09 Mon Sep 17 00:00:00 2001 From: Paul Burns Date: Wed, 2 Sep 2026 09:07:56 -0400 Subject: [PATCH 5/5] Name a released compiler in the warning guard impl (ghc <= 9.3) and impl (ghc < 9.4) select the same compilers, since 9.3 was a development version and was never released. Spell the boundary with a version that exists so it does not read as a version being tested. Co-Authored-By: Claude Opus 5 (1M context) --- example/example.cabal | 4 ++-- example/package.yaml | 4 ++-- ftp-client-conduit/ftp-client-conduit.cabal | 4 ++-- ftp-client-conduit/package.yaml | 4 ++-- ftp-client/ftp-client.cabal | 4 ++-- ftp-client/package.yaml | 4 ++-- 6 files changed, 12 insertions(+), 12 deletions(-) diff --git a/example/example.cabal b/example/example.cabal index ca64685..195d6db 100644 --- a/example/example.cabal +++ b/example/example.cabal @@ -39,7 +39,7 @@ executable example-exe default-language: Haskell2010 if flag(ci) ghc-options: -O2 -threaded -rtsopts -with-rtsopts=-N -Wall -Werror -Wcompat -Widentities -Winvalid-haddock -Wmissing-local-signatures -Wmissing-export-lists -Wpartial-fields -Wmissed-specialisations -Wno-implicit-prelude -Wno-safe -Wno-unsafe -Wunused-packages - if impl (ghc <= 9.3) + if impl (ghc < 9.4) ghc-options: -Wno-unticked-promoted-constructors if impl (ghc >= 9.4) ghc-options: -Wimplicit-lift -Woperator-whitespace -Wredundant-bang-patterns -Wredundant-strictness-flags @@ -62,7 +62,7 @@ test-suite example-test default-language: Haskell2010 if flag(ci) ghc-options: -threaded -rtsopts -with-rtsopts=-N -Wall -Werror -Wcompat -Widentities -Winvalid-haddock -Wmissing-local-signatures -Wmissing-export-lists -Wpartial-fields -Wmissed-specialisations -Wno-implicit-prelude -Wno-safe -Wno-unsafe - if impl (ghc <= 9.3) + if impl (ghc < 9.4) ghc-options: -Wno-unticked-promoted-constructors if impl (ghc >= 9.4) ghc-options: -Wimplicit-lift -Woperator-whitespace -Wredundant-bang-patterns -Wredundant-strictness-flags diff --git a/example/package.yaml b/example/package.yaml index 1fa2bc9..e613db0 100644 --- a/example/package.yaml +++ b/example/package.yaml @@ -49,7 +49,7 @@ executables: - -Wno-unsafe - -Wunused-packages when: - - condition: impl (ghc <= 9.3) + - condition: impl (ghc < 9.4) ghc-options: - -Wno-unticked-promoted-constructors - condition: impl (ghc >= 9.4) @@ -99,7 +99,7 @@ tests: - -Wno-safe - -Wno-unsafe when: - - condition: impl (ghc <= 9.3) + - condition: impl (ghc < 9.4) ghc-options: - -Wno-unticked-promoted-constructors - condition: impl (ghc >= 9.4) diff --git a/ftp-client-conduit/ftp-client-conduit.cabal b/ftp-client-conduit/ftp-client-conduit.cabal index bf47c60..37197e0 100644 --- a/ftp-client-conduit/ftp-client-conduit.cabal +++ b/ftp-client-conduit/ftp-client-conduit.cabal @@ -55,7 +55,7 @@ library default-language: Haskell2010 if flag(ci) ghc-options: -O2 -Wall -Werror -Wcompat -Widentities -Winvalid-haddock -Wmissing-local-signatures -Wmissing-export-lists -Wpartial-fields -Wmissed-specialisations -Wno-implicit-prelude -Wno-safe -Wno-unsafe -Wunused-packages - if impl (ghc <= 9.3) + if impl (ghc < 9.4) ghc-options: -Wno-unticked-promoted-constructors if impl (ghc >= 9.4) ghc-options: -Wimplicit-lift -Woperator-whitespace -Wredundant-bang-patterns -Wredundant-strictness-flags @@ -77,7 +77,7 @@ test-suite ftp-conduit-test default-language: Haskell2010 if flag(ci) ghc-options: -threaded -rtsopts -with-rtsopts=-N -Wall -Werror -Wcompat -Widentities -Winvalid-haddock -Wmissing-local-signatures -Wmissing-export-lists -Wpartial-fields -Wmissed-specialisations -Wno-implicit-prelude -Wno-safe -Wno-unsafe - if impl (ghc <= 9.3) + if impl (ghc < 9.4) ghc-options: -Wno-unticked-promoted-constructors if impl (ghc >= 9.4) ghc-options: -Wimplicit-lift -Woperator-whitespace -Wredundant-bang-patterns -Wredundant-strictness-flags diff --git a/ftp-client-conduit/package.yaml b/ftp-client-conduit/package.yaml index eee8671..ec1f36c 100644 --- a/ftp-client-conduit/package.yaml +++ b/ftp-client-conduit/package.yaml @@ -65,7 +65,7 @@ library: # Previously the guidance was to always use ticked constructors for type # constructors, but that guidance has been reversed, so we turn the # warning off in older compilers. - - condition: impl (ghc <= 9.3) + - condition: impl (ghc < 9.4) ghc-options: - -Wno-unticked-promoted-constructors - condition: impl (ghc >= 9.4) @@ -111,7 +111,7 @@ tests: - -Wno-safe - -Wno-unsafe when: - - condition: impl (ghc <= 9.3) + - condition: impl (ghc < 9.4) ghc-options: - -Wno-unticked-promoted-constructors - condition: impl (ghc >= 9.4) diff --git a/ftp-client/ftp-client.cabal b/ftp-client/ftp-client.cabal index c6993fc..902d7d8 100644 --- a/ftp-client/ftp-client.cabal +++ b/ftp-client/ftp-client.cabal @@ -56,7 +56,7 @@ library default-language: Haskell2010 if flag(ci) ghc-options: -O2 -Wall -Werror -Wcompat -Widentities -Winvalid-haddock -Wmissing-local-signatures -Wmissing-export-lists -Wpartial-fields -Wmissed-specialisations -Wno-implicit-prelude -Wno-safe -Wno-unsafe -Wunused-packages - if impl (ghc <= 9.3) + if impl (ghc < 9.4) ghc-options: -Wno-unticked-promoted-constructors if impl (ghc >= 9.4) ghc-options: -Wimplicit-lift -Woperator-whitespace -Wredundant-bang-patterns -Wredundant-strictness-flags @@ -80,7 +80,7 @@ test-suite ftp-client-test default-language: Haskell2010 if flag(ci) ghc-options: -threaded -rtsopts -with-rtsopts=-N -Wall -Werror -Wcompat -Widentities -Winvalid-haddock -Wmissing-local-signatures -Wmissing-export-lists -Wpartial-fields -Wmissed-specialisations -Wno-implicit-prelude -Wno-safe -Wno-unsafe - if impl (ghc <= 9.3) + if impl (ghc < 9.4) ghc-options: -Wno-unticked-promoted-constructors if impl (ghc >= 9.4) ghc-options: -Wimplicit-lift -Woperator-whitespace -Wredundant-bang-patterns -Wredundant-strictness-flags diff --git a/ftp-client/package.yaml b/ftp-client/package.yaml index 70400cc..4d71c6b 100644 --- a/ftp-client/package.yaml +++ b/ftp-client/package.yaml @@ -66,7 +66,7 @@ library: # Previously the guidance was to always use ticked constructors for type # constructors, but that guidance has been reversed, so we turn the # warning off in older compilers. - - condition: impl (ghc <= 9.3) + - condition: impl (ghc < 9.4) ghc-options: - -Wno-unticked-promoted-constructors - condition: impl (ghc >= 9.4) @@ -117,7 +117,7 @@ tests: # Previously the guidance was to always use ticked constructors for type # constructors, but that guidance has been reversed, so we turn the # warning off in older compilers. - - condition: impl (ghc <= 9.3) + - condition: impl (ghc < 9.4) ghc-options: - -Wno-unticked-promoted-constructors - condition: impl (ghc >= 9.4)