language-docker 11.0.0 → 12.0.0
raw patch · 9 files changed
+264/−76 lines, 9 files
Files
- language-docker.cabal +7/−3
- src/Language/Docker/Parser/Expose.hs +33/−7
- src/Language/Docker/PrettyPrint.hs +4/−7
- src/Language/Docker/Syntax.hs +18/−17
- src/Language/Docker/Syntax/Port.hs +19/−0
- src/Language/Docker/Syntax/PortRange.hs +20/−0
- src/Language/Docker/Syntax/Protocol.hs +15/−0
- test/Language/Docker/ParseExposeSpec.hs +148/−0
- test/Language/Docker/ParserSpec.hs +0/−42
language-docker.cabal view
@@ -1,13 +1,13 @@ cabal-version: 1.12 --- This file has been generated from package.yaml by hpack version 0.34.4.+-- This file has been generated from package.yaml by hpack version 0.34.7. -- -- see: https://github.com/sol/hpack ----- hash: 1dcabc3e868541acb7c49590054c62c6637d1deaefc81e4b39de64333bc9d239+-- hash: f800aba94b9f6bbf2894ac78e2f4a135d3e9e89e04d9a7b0549bdd0941e97cbd name: language-docker-version: 11.0.0+version: 12.0.0 synopsis: Dockerfile parser, pretty-printer and embedded DSL description: All functions for parsing and pretty-printing Dockerfiles are exported through @Language.Docker@. For more fine-grained operations look for specific modules that implement a certain functionality. See the <https://github.com/hadolint/language-docker GitHub project> for the source-code and examples.@@ -61,6 +61,9 @@ Language.Docker.Parser.Pairs Language.Docker.Parser.Prelude Language.Docker.Parser.Run+ Language.Docker.Syntax.Port+ Language.Docker.Syntax.PortRange+ Language.Docker.Syntax.Protocol Paths_language_docker hs-source-dirs: src@@ -90,6 +93,7 @@ Language.Docker.ParseAddSpec Language.Docker.ParseCmdSpec Language.Docker.ParseCopySpec+ Language.Docker.ParseExposeSpec Language.Docker.ParsePragmaSpec Language.Docker.ParserSpec Language.Docker.ParseRunSpec
src/Language/Docker/Parser/Expose.hs view
@@ -12,22 +12,48 @@ reserved "EXPOSE" Expose <$> ports +ports :: (?esc :: Char) => Parser Ports+ports = Ports <$> portspec `sepEndBy` requiredWhitespace++portspec :: (?esc :: Char) => Parser PortSpec+portspec =+ ( try parsePortRangeSpec <?> "A range of ports optionally followed by the protocol" )+ <|> ( parsePortSpec <?> "A port optionally followed by the protocol" )++parsePortRangeSpec :: (?esc :: Char) => Parser PortSpec+parsePortRangeSpec = PortRangeSpec <$> portRange++parsePortSpec :: (?esc :: Char) => Parser PortSpec+parsePortSpec = PortSpec <$> port+ port :: (?esc :: Char) => Parser Port port = (try portVariable <?> "a variable")- <|> (try portRange <?> "a port range optionally followed by the protocol (udp/tcp)") -- There a many valid representations of ports <|> (try portWithProtocol <?> "a port with its protocol (udp/tcp)") <|> (portInt <?> "a valid port number") -ports :: (?esc :: Char) => Parser Ports-ports = Ports <$> port `sepEndBy` requiredWhitespace+portRangeLimit :: (?esc :: Char) => Parser Port+portRangeLimit = number <|> variable+ where+ number = do+ num <- natural+ return $ Port (fromIntegral num) TCP -portRange :: Parser Port+ variable = do+ void (char '$')+ var <- someUnless "the variable name" (\c -> c == '-' || c == '/')+ return $ PortStr (T.append "$" var)++portRange :: (?esc :: Char) => Parser PortRange portRange = do- start <- natural+ start <- portRangeLimit void $ char '-'- finish <- try natural+ finish <- try portRangeLimit proto <- try protocol <|> return TCP- return $ PortRange (fromIntegral start) (fromIntegral finish) proto+ return $ PortRange (setProto start proto) (setProto finish proto)+ where+ setProto :: Port -> Protocol -> Port+ setProto (Port p _) prot = Port p prot+ setProto (PortStr s) _ = PortStr s protocol :: Parser Protocol protocol = do
src/Language/Docker/PrettyPrint.hs view
@@ -123,12 +123,9 @@ accumulate c EscapeAccum {buffer, escaping = False} = EscapeAccum (B.singleton c <> buffer) 0 False -prettyPrintPort :: Port -> Doc ann-prettyPrintPort (PortStr str) = pretty str-prettyPrintPort (PortRange start stop TCP) = pretty start <> "-" <> pretty stop-prettyPrintPort (PortRange start stop UDP) = pretty start <> "-" <> pretty stop <> "/udp"-prettyPrintPort (Port num TCP) = pretty num <> "/tcp"-prettyPrintPort (Port num UDP) = pretty num <> "/udp"+prettyPrintPortSpec :: PortSpec -> Doc ann+prettyPrintPortSpec (PortSpec port) = pretty port+prettyPrintPortSpec (PortRangeSpec portrange) = pretty portrange prettyPrintFileList :: NonEmpty SourcePath -> TargetPath -> Doc ann prettyPrintFileList sources (TargetPath dest) =@@ -273,7 +270,7 @@ pretty w Expose (Ports ps) -> do "EXPOSE"- hsep (fmap prettyPrintPort ps)+ hsep (fmap prettyPrintPortSpec ps) Volume dir -> do "VOLUME" pretty dir
src/Language/Docker/Syntax.hs view
@@ -4,7 +4,13 @@ {-# LANGUAGE GeneralizedNewtypeDeriving #-} {-# LANGUAGE TypeFamilies #-} -module Language.Docker.Syntax where+module Language.Docker.Syntax+ ( module Language.Docker.Syntax,+ module Language.Docker.Syntax.Port,+ module Language.Docker.Syntax.PortRange,+ module Language.Docker.Syntax.Protocol+ )+where import Data.Default.Class (Default (..)) import Data.List (intercalate, isInfixOf)@@ -18,6 +24,12 @@ import GHC.Exts (IsList (..)) import Text.Printf ++import Language.Docker.Syntax.Port+import Language.Docker.Syntax.PortRange+import Language.Docker.Syntax.Protocol++ data Image = Image { registryName :: !(Maybe Registry),@@ -56,30 +68,19 @@ } deriving (Show, Eq, Ord, IsString) -data Protocol- = TCP- | UDP- deriving (Show, Eq, Ord)--data Port- = Port- !Int- !Protocol- | PortStr !Text- | PortRange- !Int- !Int- !Protocol+data PortSpec+ = PortSpec !Port+ | PortRangeSpec !PortRange deriving (Show, Eq, Ord) newtype Ports = Ports- { unPorts :: [Port]+ { unPorts :: [PortSpec] } deriving (Show, Eq, Ord) instance IsList Ports where- type Item Ports = Port+ type Item Ports = PortSpec fromList = Ports toList (Ports ps) = ps
+ src/Language/Docker/Syntax/Port.hs view
@@ -0,0 +1,19 @@+module Language.Docker.Syntax.Port where+++import Data.Text+import Prettyprinter+import Language.Docker.Syntax.Protocol+++-- | A port can either be a number (plus a protocol, tcp by default) or a+-- variable.+data Port+ = Port !Int !Protocol+ | PortStr !Text+ deriving (Show, Eq, Ord)+++instance Pretty Port where+ pretty (Port num proto) = pretty num <> pretty proto+ pretty (PortStr str) = pretty str
+ src/Language/Docker/Syntax/PortRange.hs view
@@ -0,0 +1,20 @@+module Language.Docker.Syntax.PortRange where+++import Prettyprinter+import Language.Docker.Syntax.Port+import Language.Docker.Syntax.Protocol+++-- | A port range starts and ends with either a number or a variable and can+-- have a protocol associated (tcp by default). The protocol of the start and+-- end port shall be ignored.+data PortRange+ = PortRange !Port !Port+ deriving (Show, Eq, Ord)+++instance Pretty PortRange where+ pretty (PortRange (Port start UDP) end) = pretty start <> "-" <> pretty end <> "/udp"+ pretty (PortRange (PortStr start) (Port end UDP)) = pretty start <> "-" <> pretty end <> "/udp"+ pretty (PortRange start end) = pretty start <> "-" <> pretty end
+ src/Language/Docker/Syntax/Protocol.hs view
@@ -0,0 +1,15 @@+module Language.Docker.Syntax.Protocol where+++import Prettyprinter+++data Protocol+ = TCP+ | UDP+ deriving (Show, Eq, Ord)+++instance Pretty Protocol where+ pretty TCP = ""+ pretty UDP = "/udp"
+ test/Language/Docker/ParseExposeSpec.hs view
@@ -0,0 +1,148 @@+module Language.Docker.ParseExposeSpec where++import Data.Default.Class (def)+import qualified Data.Text as Text+import Language.Docker.Syntax+import TestHelper+import Test.Hspec+++spec :: Spec+spec = do+ describe "parse EXPOSE" $ do++ it "should handle number ports" $+ let content = "EXPOSE 8080"+ in assertAst+ content+ [ Expose+ ( Ports+ [ PortSpec (Port 8080 TCP)+ ]+ )+ ]++ it "should handle many number ports" $+ let content = "EXPOSE 8080 8081"+ in assertAst+ content+ [ Expose+ ( Ports+ [ PortSpec (Port 8080 TCP),+ PortSpec (Port 8081 TCP)+ ]+ )+ ]++ it "should handle ports with protocol" $+ let content = "EXPOSE 8080/TCP 8081/UDP"+ in assertAst+ content+ [ Expose+ ( Ports+ [ PortSpec (Port 8080 TCP),+ PortSpec (Port 8081 UDP)+ ]+ )+ ]++ it "should handle ports with protocol and variables" $+ let content = "EXPOSE $PORT 8080 8081/UDP"+ in assertAst+ content+ [ Expose+ ( Ports+ [ PortSpec (PortStr "$PORT"),+ PortSpec (Port 8080 TCP),+ PortSpec (Port 8081 UDP)+ ]+ )+ ]++ it "should handle port ranges" $+ let content = "EXPOSE 80 81 8080-8085"+ in assertAst+ content+ [ Expose+ ( Ports+ [ PortSpec (Port 80 TCP),+ PortSpec (Port 81 TCP),+ PortRangeSpec+ ( PortRange (Port 8080 TCP) (Port 8085 TCP) )+ ]+ )+ ]++ it "should handle udp port ranges" $+ let content = "EXPOSE 80 81 8080-8085/udp"+ in assertAst+ content+ [ Expose+ ( Ports+ [ PortSpec (Port 80 TCP),+ PortSpec (Port 81 TCP),+ PortRangeSpec+ ( PortRange (Port 8080 UDP) (Port 8085 UDP) )+ ]+ )+ ]++ it "should handle one variable port ranges 1" $+ let content = "EXPOSE 8080-${bar}/udp"+ in assertAst+ content+ [ Expose+ ( Ports+ [ PortRangeSpec+ ( PortRange (Port 8080 UDP) (PortStr "${bar}") )+ ]+ )+ ]++ it "should handle one variable port ranges 2" $+ let content = "EXPOSE ${foo}-8080/udp"+ in assertAst+ content+ [ Expose+ ( Ports+ [ PortRangeSpec+ ( PortRange (PortStr "${foo}") (Port 8080 UDP) )+ ]+ )+ ]++ it "should handle two variables port ranges" $+ let content = "EXPOSE ${foo}-${bar}/udp"+ in assertAst+ content+ [ Expose+ ( Ports+ [ PortRangeSpec+ ( PortRange (PortStr "${foo}") (PortStr "${bar}") )+ ]+ )+ ]++ it "should handle multiline variables" $+ let content =+ "EXPOSE ${PORT} ${PORT_SSL} \\\n\+ \ ${PORT_HTTP} ${PORT_HTTPS} \\\n\+ \ ${PORT_REP} \\\n\+ \ ${PORT_ADMIN} ${PORT_ADMIN_HTTP}"+ in assertAst+ content+ [ Expose+ ( Ports+ [ PortSpec (PortStr "${PORT}"),+ PortSpec (PortStr "${PORT_SSL}"),+ PortSpec (PortStr "${PORT_HTTP}"),+ PortSpec (PortStr "${PORT_HTTPS}"),+ PortSpec (PortStr "${PORT_REP}"),+ PortSpec (PortStr "${PORT_ADMIN}"),+ PortSpec (PortStr "${PORT_ADMIN_HTTP}")+ ]+ )+ ]+ it "should fail with wrong protocol" $+ let content = "EXPOSE 80/ip"+ in expectFail content
test/Language/Docker/ParserSpec.hs view
@@ -289,48 +289,6 @@ Comment " This is a comment", Run "echo hello" ]- describe "expose" $ do- it "should handle number ports" $- let content = "EXPOSE 8080"- in assertAst content [Expose (Ports [Port 8080 TCP])]- it "should handle many number ports" $- let content = "EXPOSE 8080 8081"- in assertAst content [Expose (Ports [Port 8080 TCP, Port 8081 TCP])]- it "should handle ports with protocol" $- let content = "EXPOSE 8080/TCP 8081/UDP"- in assertAst content [Expose (Ports [Port 8080 TCP, Port 8081 UDP])]- it "should handle ports with protocol and variables" $- let content = "EXPOSE $PORT 8080 8081/UDP"- in assertAst content [Expose (Ports [PortStr "$PORT", Port 8080 TCP, Port 8081 UDP])]- it "should handle port ranges" $- let content = "EXPOSE 80 81 8080-8085"- in assertAst content [Expose (Ports [Port 80 TCP, Port 81 TCP, PortRange 8080 8085 TCP])]- it "should handle udp port ranges" $- let content = "EXPOSE 80 81 8080-8085/udp"- in assertAst content [Expose (Ports [Port 80 TCP, Port 81 TCP, PortRange 8080 8085 UDP])]- it "should handle multiline variables" $- let content =- "EXPOSE ${PORT} ${PORT_SSL} \\\n\- \ ${PORT_HTTP} ${PORT_HTTPS} \\\n\- \ ${PORT_REP} \\\n\- \ ${PORT_ADMIN} ${PORT_ADMIN_HTTP}"- in assertAst- content- [ Expose- ( Ports- [ PortStr "${PORT}",- PortStr "${PORT_SSL}",- PortStr "${PORT_HTTP}",- PortStr "${PORT_HTTPS}",- PortStr "${PORT_REP}",- PortStr "${PORT_ADMIN}",- PortStr "${PORT_ADMIN_HTTP}"- ]- )- ]- it "should fail with wrong protocol" $- let content = "EXPOSE 80/ip"- in expectFail content describe "syntax" $ do it "should handle lowercase instructions (#7 - https://github.com/beijaflor-io/haskell-language-dockerfile/issues/7)" $ let content = "from ubuntu"