Extern tag matcher with digit and dot patterns
This commit is contained in:
parent
7e59a8460d
commit
7c9c890056
@ -11,7 +11,6 @@ module SlackBuilder.LatestVersionCheck
|
|||||||
, latestPackagist
|
, latestPackagist
|
||||||
, latestText
|
, latestText
|
||||||
, match
|
, match
|
||||||
, stableTagTransform
|
|
||||||
) where
|
) where
|
||||||
|
|
||||||
import SlackBuilder.Config
|
import SlackBuilder.Config
|
||||||
@ -47,22 +46,12 @@ import qualified Data.Aeson.KeyMap as KeyMap
|
|||||||
import GHC.Records (HasField(..))
|
import GHC.Records (HasField(..))
|
||||||
import Control.Monad.Trans.Reader (asks)
|
import Control.Monad.Trans.Reader (asks)
|
||||||
import Control.Monad.IO.Class (MonadIO(..))
|
import Control.Monad.IO.Class (MonadIO(..))
|
||||||
import Control.Monad ((>=>))
|
|
||||||
|
|
||||||
data PackageOwner = PackageOwner
|
data PackageOwner = PackageOwner
|
||||||
{ owner :: Text
|
{ owner :: Text
|
||||||
, name :: Text
|
, name :: Text
|
||||||
} deriving (Eq, Show)
|
} deriving (Eq, Show)
|
||||||
|
|
||||||
-- | Removes the leading "v" from the version string and returns the result if
|
|
||||||
-- it looks like a version.
|
|
||||||
stableTagTransform :: Text -> Maybe Text
|
|
||||||
stableTagTransform = Text.stripPrefix "v" >=> checkForStable
|
|
||||||
where
|
|
||||||
checkForStable tag
|
|
||||||
| Text.any (`elem` ['-', '+']) tag = Nothing
|
|
||||||
| otherwise = Just tag
|
|
||||||
|
|
||||||
data MatchState = MatchState
|
data MatchState = MatchState
|
||||||
{ ignoring :: Bool
|
{ ignoring :: Bool
|
||||||
, matched :: Text
|
, matched :: Text
|
||||||
@ -72,28 +61,43 @@ data MatchState = MatchState
|
|||||||
data MatchToken
|
data MatchToken
|
||||||
= OpenParenMatchToken
|
= OpenParenMatchToken
|
||||||
| CloseParenMatchToken
|
| CloseParenMatchToken
|
||||||
| AsteriskMatchToken (Maybe Char)
|
| AsteriskMatchToken
|
||||||
| SymbolMatchToken Char
|
| SymbolMatchToken Char
|
||||||
|
| OneOfMatchToken [Char]
|
||||||
deriving (Eq, Show)
|
deriving (Eq, Show)
|
||||||
|
|
||||||
|
-- | Removes the leading "v" from the version string and returns the result if
|
||||||
|
-- it looks like a version.
|
||||||
match :: Text -> Text -> Maybe Text
|
match :: Text -> Text -> Maybe Text
|
||||||
match fullPattern input =
|
match fullPattern input =
|
||||||
case Text.foldl' go (Just startState) input of
|
case Text.foldl' go (Just startState) input of
|
||||||
Just state@MatchState{ pattern' = [] } -> Just $ getField @"matched" state
|
Just state@MatchState{ pattern' = [] } -> Just $ getField @"matched" state
|
||||||
Just state@MatchState{ pattern' = [CloseParenMatchToken] } ->
|
Just state@MatchState{ pattern' = [CloseParenMatchToken] } ->
|
||||||
Just $ getField @"matched" state
|
Just $ getField @"matched" state
|
||||||
Just state@MatchState{ pattern' = [AsteriskMatchToken _] } ->
|
Just state@MatchState{ pattern' = [AsteriskMatchToken] } ->
|
||||||
|
Just $ getField @"matched" state
|
||||||
|
Just state@MatchState{ pattern' = [OneOfMatchToken _] } ->
|
||||||
Just $ getField @"matched" state
|
Just $ getField @"matched" state
|
||||||
_ -> Nothing
|
_ -> Nothing
|
||||||
where
|
where
|
||||||
reserved = ['*', '(', ')']
|
digits = toEnum <$> [fromEnum '0' .. fromEnum '9']
|
||||||
parsePattern :: Text -> [MatchToken]
|
parsePattern :: Text -> [MatchToken]
|
||||||
parsePattern input'
|
parsePattern input'
|
||||||
|
| Just (firstChar, remaining) <- Text.uncons input'
|
||||||
|
, firstChar == '\\' =
|
||||||
|
case Text.uncons remaining of
|
||||||
|
Nothing -> []
|
||||||
|
Just ('d', remaining') -> OneOfMatchToken digits
|
||||||
|
: parsePattern remaining'
|
||||||
|
Just ('.', remaining') -> OneOfMatchToken ('.' : digits)
|
||||||
|
: parsePattern remaining'
|
||||||
|
Just ('\\', remaining') -> SymbolMatchToken '\\'
|
||||||
|
: parsePattern remaining'
|
||||||
|
Just (_, remaining') -> parsePattern remaining'
|
||||||
| Just (firstChar, remaining) <- Text.uncons input' =
|
| Just (firstChar, remaining) <- Text.uncons input' =
|
||||||
let token =
|
let token =
|
||||||
case firstChar of
|
case firstChar of
|
||||||
'*' -> AsteriskMatchToken
|
'*' -> AsteriskMatchToken
|
||||||
$ Text.find (not . (`elem` reserved)) remaining
|
|
||||||
'(' -> OpenParenMatchToken
|
'(' -> OpenParenMatchToken
|
||||||
')' -> CloseParenMatchToken
|
')' -> CloseParenMatchToken
|
||||||
s -> SymbolMatchToken s
|
s -> SymbolMatchToken s
|
||||||
@ -109,14 +113,16 @@ match fullPattern input =
|
|||||||
case token of
|
case token of
|
||||||
OpenParenMatchToken -> go (Just state{ ignoring = True, pattern' = remaining }) nextCharacter
|
OpenParenMatchToken -> go (Just state{ ignoring = True, pattern' = remaining }) nextCharacter
|
||||||
CloseParenMatchToken -> go (Just state{ ignoring = False, pattern' = remaining }) nextCharacter
|
CloseParenMatchToken -> go (Just state{ ignoring = False, pattern' = remaining }) nextCharacter
|
||||||
AsteriskMatchToken stopChar
|
AsteriskMatchToken -> Just $ matchSymbolToken state nextCharacter
|
||||||
| Just nextCharacter == stopChar ->
|
|
||||||
go (Just state{ pattern' = remaining }) nextCharacter
|
|
||||||
| otherwise -> Just $ matchSymbolToken state nextCharacter
|
|
||||||
SymbolMatchToken patternCharacter
|
SymbolMatchToken patternCharacter
|
||||||
| patternCharacter == nextCharacter -> Just
|
| patternCharacter == nextCharacter -> Just
|
||||||
$ matchSymbolToken state{ pattern' = remaining } nextCharacter
|
$ matchSymbolToken state{ pattern' = remaining } nextCharacter
|
||||||
| otherwise -> Nothing
|
| otherwise -> Nothing
|
||||||
|
OneOfMatchToken chars
|
||||||
|
| nextCharacter `elem` chars ->
|
||||||
|
Just $ matchSymbolToken state nextCharacter
|
||||||
|
| otherwise ->
|
||||||
|
go (Just state{ pattern' = remaining }) nextCharacter
|
||||||
go _ _ = Nothing
|
go _ _ = Nothing
|
||||||
matchSymbolToken state nextCharacter
|
matchSymbolToken state nextCharacter
|
||||||
| getField @"ignoring" state = state
|
| getField @"ignoring" state = state
|
||||||
@ -214,9 +220,9 @@ $(deriveJSON defaultOptions ''GhQuery)
|
|||||||
|
|
||||||
latestGitHub
|
latestGitHub
|
||||||
:: PackageOwner
|
:: PackageOwner
|
||||||
-> (Text -> Maybe Text)
|
-> Text
|
||||||
-> SlackBuilderT (Maybe Text)
|
-> SlackBuilderT (Maybe Text)
|
||||||
latestGitHub PackageOwner{..} versionTransform = do
|
latestGitHub PackageOwner{..} pattern' = do
|
||||||
ghToken' <- SlackBuilderT $ asks ghToken
|
ghToken' <- SlackBuilderT $ asks ghToken
|
||||||
ghResponse <- runReq defaultHttpConfig $
|
ghResponse <- runReq defaultHttpConfig $
|
||||||
let uri = https "api.github.com" /: "graphql"
|
let uri = https "api.github.com" /: "graphql"
|
||||||
@ -238,14 +244,13 @@ latestGitHub PackageOwner{..} versionTransform = do
|
|||||||
$ responseBody ghResponse
|
$ responseBody ghResponse
|
||||||
refs' = Vector.reverse
|
refs' = Vector.reverse
|
||||||
$ Vector.catMaybes
|
$ Vector.catMaybes
|
||||||
$ versionTransform . getField @"name" <$> ghNodes
|
$ match pattern' . getField @"name" <$> ghNodes
|
||||||
liftIO $ print $ getField @"name" <$> ghNodes
|
|
||||||
pure $ refs' !? 0
|
pure $ refs' !? 0
|
||||||
where
|
where
|
||||||
githubQuery =
|
githubQuery =
|
||||||
"query ($name: String!, $owner: String!) {\n\
|
"query ($name: String!, $owner: String!) {\n\
|
||||||
\ repository(name: $name, owner: $owner) {\n\
|
\ repository(name: $name, owner: $owner) {\n\
|
||||||
\ refs(last: 10, query: \"!(RC3)\", refPrefix: \"refs/tags/\", orderBy: { field: TAG_COMMIT_DATE, direction: ASC }) {\n\
|
\ refs(last: 10, refPrefix: \"refs/tags/\", orderBy: { field: TAG_COMMIT_DATE, direction: ASC }) {\n\
|
||||||
\ nodes {\n\
|
\ nodes {\n\
|
||||||
\ id,\n\
|
\ id,\n\
|
||||||
\ name\n\
|
\ name\n\
|
||||||
|
22
src/Main.hs
22
src/Main.hs
@ -45,7 +45,7 @@ autoUpdatable =
|
|||||||
$ Package.StaticPlaceholder "https://github.com/universal-ctags/ctags/archive/"
|
$ Package.StaticPlaceholder "https://github.com/universal-ctags/ctags/archive/"
|
||||||
:| templateTail
|
:| templateTail
|
||||||
in Package.Updater
|
in Package.Updater
|
||||||
{ detectLatest = latestGitHub ghArguments stableTagTransform
|
{ detectLatest = latestGitHub ghArguments "(v)\\."
|
||||||
, getVersion = reuploadWithTemplate template []
|
, getVersion = reuploadWithTemplate template []
|
||||||
, is64 = False
|
, is64 = False
|
||||||
}
|
}
|
||||||
@ -77,7 +77,7 @@ autoUpdatable =
|
|||||||
:| Package.VersionPlaceholder
|
:| Package.VersionPlaceholder
|
||||||
: [Package.StaticPlaceholder "/jitsi-meet-x86_64.AppImage"]
|
: [Package.StaticPlaceholder "/jitsi-meet-x86_64.AppImage"]
|
||||||
in Package.Updater
|
in Package.Updater
|
||||||
{ detectLatest = latestGitHub ghArguments $ Text.stripPrefix "v"
|
{ detectLatest = latestGitHub ghArguments "(v)*"
|
||||||
, getVersion = downloadWithTemplate template
|
, getVersion = downloadWithTemplate template
|
||||||
, is64 = True
|
, is64 = True
|
||||||
}
|
}
|
||||||
@ -90,16 +90,12 @@ autoUpdatable =
|
|||||||
{ owner = "php"
|
{ owner = "php"
|
||||||
, name = "php-src"
|
, name = "php-src"
|
||||||
}
|
}
|
||||||
checkVersion x
|
|
||||||
| not $ Text.isInfixOf "RC" x
|
|
||||||
, Text.isPrefixOf "php-8.2." x = Text.stripPrefix "php-" x
|
|
||||||
| otherwise = Nothing
|
|
||||||
template = Package.DownloadTemplate
|
template = Package.DownloadTemplate
|
||||||
$ Package.StaticPlaceholder "https://www.php.net/distributions/php-"
|
$ Package.StaticPlaceholder "https://www.php.net/distributions/php-"
|
||||||
:| Package.VersionPlaceholder
|
:| Package.VersionPlaceholder
|
||||||
: [Package.StaticPlaceholder ".tar.xz"]
|
: [Package.StaticPlaceholder ".tar.xz"]
|
||||||
in Package.Updater
|
in Package.Updater
|
||||||
{ detectLatest = latestGitHub ghArguments checkVersion
|
{ detectLatest = latestGitHub ghArguments "(php-)8.2.\\d"
|
||||||
, getVersion = downloadWithTemplate template
|
, getVersion = downloadWithTemplate template
|
||||||
, is64 = False
|
, is64 = False
|
||||||
}
|
}
|
||||||
@ -122,7 +118,7 @@ autoUpdatable =
|
|||||||
:| Package.VersionPlaceholder
|
:| Package.VersionPlaceholder
|
||||||
: templateTail
|
: templateTail
|
||||||
in Package.Updater
|
in Package.Updater
|
||||||
{ detectLatest = latestGitHub ghArguments stableTagTransform
|
{ detectLatest = latestGitHub ghArguments "(v)\\."
|
||||||
, getVersion = reuploadWithTemplate template [RawCommand "go" ["mod", "vendor"]]
|
, getVersion = reuploadWithTemplate template [RawCommand "go" ["mod", "vendor"]]
|
||||||
, is64 = False
|
, is64 = False
|
||||||
}
|
}
|
||||||
@ -142,7 +138,7 @@ autoUpdatable =
|
|||||||
: Package.VersionPlaceholder
|
: Package.VersionPlaceholder
|
||||||
: [Package.StaticPlaceholder ".tar.gz"]
|
: [Package.StaticPlaceholder ".tar.gz"]
|
||||||
in Package.Updater
|
in Package.Updater
|
||||||
{ detectLatest = latestGitHub ghArguments stableTagTransform
|
{ detectLatest = latestGitHub ghArguments "(v)\\."
|
||||||
, getVersion = reuploadWithTemplate template []
|
, getVersion = reuploadWithTemplate template []
|
||||||
, is64 = False
|
, is64 = False
|
||||||
}
|
}
|
||||||
@ -183,7 +179,7 @@ autoUpdatable =
|
|||||||
: Package.VersionPlaceholder
|
: Package.VersionPlaceholder
|
||||||
: [Package.StaticPlaceholder ".tar.gz"]
|
: [Package.StaticPlaceholder ".tar.gz"]
|
||||||
in Package.Updater
|
in Package.Updater
|
||||||
{ detectLatest = latestGitHub ghArguments $ Text.stripPrefix "v"
|
{ detectLatest = latestGitHub ghArguments "(v)\\."
|
||||||
, getVersion = reuploadWithTemplate template []
|
, getVersion = reuploadWithTemplate template []
|
||||||
, is64 = True
|
, is64 = True
|
||||||
}
|
}
|
||||||
@ -230,18 +226,18 @@ autoUpdatable =
|
|||||||
dscannerArguments = PackageOwner{ owner = "dlang-community", name = "D-Scanner" }
|
dscannerArguments = PackageOwner{ owner = "dlang-community", name = "D-Scanner" }
|
||||||
dcdArguments = PackageOwner{ owner = "dlang-community", name = "DCD" }
|
dcdArguments = PackageOwner{ owner = "dlang-community", name = "DCD" }
|
||||||
latestDub = Package.Updater
|
latestDub = Package.Updater
|
||||||
{ detectLatest = latestGitHub dubArguments stableTagTransform
|
{ detectLatest = latestGitHub dubArguments "(v)\\."
|
||||||
, getVersion = downloadWithTemplate dubTemplate
|
, getVersion = downloadWithTemplate dubTemplate
|
||||||
, is64 = False
|
, is64 = False
|
||||||
}
|
}
|
||||||
latestDscanner = Package.Updater
|
latestDscanner = Package.Updater
|
||||||
{ detectLatest = latestGitHub dscannerArguments stableTagTransform
|
{ detectLatest = latestGitHub dscannerArguments "(v)\\."
|
||||||
, getVersion = cloneFromGit dscannerURI "v"
|
, getVersion = cloneFromGit dscannerURI "v"
|
||||||
, is64 = False
|
, is64 = False
|
||||||
}
|
}
|
||||||
dcdURI = [uri|https://github.com/dlang-community/DCD.git|]
|
dcdURI = [uri|https://github.com/dlang-community/DCD.git|]
|
||||||
latestDcd = Package.Updater
|
latestDcd = Package.Updater
|
||||||
{ detectLatest = latestGitHub dcdArguments stableTagTransform
|
{ detectLatest = latestGitHub dcdArguments "(v)\\."
|
||||||
, getVersion = cloneFromGit dcdURI "v"
|
, getVersion = cloneFromGit dcdURI "v"
|
||||||
, is64 = False
|
, is64 = False
|
||||||
}
|
}
|
||||||
|
@ -11,30 +11,33 @@ import Test.Hspec (Spec, describe, it, shouldBe)
|
|||||||
|
|
||||||
spec :: Spec
|
spec :: Spec
|
||||||
spec = do
|
spec = do
|
||||||
describe "stableTagTransform" $ do
|
|
||||||
it "excludes tags with +" $
|
|
||||||
let given = "v2.6.0+unreleased"
|
|
||||||
actual = stableTagTransform given
|
|
||||||
in actual `shouldBe` Nothing
|
|
||||||
|
|
||||||
it "recognizes a stable version" $
|
|
||||||
let given = "v2.6.0"
|
|
||||||
actual = stableTagTransform given
|
|
||||||
expected = Just "2.6.0"
|
|
||||||
in actual `shouldBe` expected
|
|
||||||
|
|
||||||
describe "match" $ do
|
describe "match" $ do
|
||||||
it "matches an exact tag prefixed with v" $
|
it "matches an exact tag prefixed with v" $
|
||||||
let expected = Just "2.6.0"
|
let expected = Just "2.6.0"
|
||||||
actual = match "(v)2.6.0" "v2.6.0"
|
actual = match "(v)2.6.0" "v2.6.0"
|
||||||
in actual `shouldBe` expected
|
in actual `shouldBe` expected
|
||||||
|
|
||||||
it "matches a glob pattern prefixed with v" $
|
it "matches a glob pattern prefixed with v" $
|
||||||
let expected = Just "2.6.0"
|
let expected = Just "2.6.0"
|
||||||
actual = match "(v)*" "v2.6.0"
|
actual = match "(v)*" "v2.6.0"
|
||||||
in actual `shouldBe` expected
|
in actual `shouldBe` expected
|
||||||
|
|
||||||
it "ignores suffix after wildcard" $
|
it "matches digits" $
|
||||||
let expected = Just "2.6.0"
|
let expected = Just "2.6.0"
|
||||||
actual = match "(v)*(-rc1)" "v2.6.0-rc1"
|
actual = match "(v)2.6.\\d" "v2.6.0"
|
||||||
|
in actual `shouldBe` expected
|
||||||
|
|
||||||
|
it "matches digits and dot" $
|
||||||
|
let expected = Just "2.6.0"
|
||||||
|
actual = match "(v)\\." "v2.6.0"
|
||||||
|
in actual `shouldBe` expected
|
||||||
|
|
||||||
|
it "rejects unexpected suffix" $
|
||||||
|
let expected = Nothing
|
||||||
|
actual = match "(v)\\." "v2.6.0-rc1"
|
||||||
|
in actual `shouldBe` expected
|
||||||
|
|
||||||
|
it "rejects remaining umatched characters" $
|
||||||
|
let expected = Nothing
|
||||||
|
actual = match "2.6.0-rc1" "2.6.0"
|
||||||
in actual `shouldBe` expected
|
in actual `shouldBe` expected
|
||||||
|
Loading…
Reference in New Issue
Block a user