diff options
Diffstat (limited to 'lib')
| -rw-r--r-- | lib/SlackBuilder/Info.hs | 156 | ||||
| -rw-r--r-- | lib/up2date.rb | 14 |
2 files changed, 156 insertions, 14 deletions
diff --git a/lib/SlackBuilder/Info.hs b/lib/SlackBuilder/Info.hs new file mode 100644 index 0000000..bedbc48 --- /dev/null +++ b/lib/SlackBuilder/Info.hs @@ -0,0 +1,156 @@ +module SlackBuilder.Info + ( PackageInfo(..) + , generate + , parseInfoFile + , update + , updateDownloadVersion + ) where + +import Control.Monad.Combinators (sepBy) +import qualified Data.ByteArray as ByteArray +import Data.ByteString (ByteString) +import qualified Data.ByteString as ByteString +import qualified Data.ByteString.Char8 as Char8 +import qualified Data.List.NonEmpty as NonEmpty +import Data.Maybe (mapMaybe) +import Data.Text (Text) +import qualified Data.Text as Text +import qualified Data.Text.Encoding as Text +import qualified Data.Text.Lazy as Lazy.Text +import qualified Data.Text.Lazy.Builder as Text.Builder +import qualified Data.Text.Lazy.Builder as Text (Builder) +import Crypto.Hash (Digest, MD5, digestFromByteString) +import Data.Void (Void) +import Data.Word (Word8) +import Numeric (readHex, showHex) +import Text.Megaparsec (Parsec, count, eof, takeWhile1P) +import Text.Megaparsec.Byte (space, string, hexDigitChar) +import Text.URI + ( Authority(..) + , URI(..) + , mkPathPiece + , parserBs + , render + , unRText + ) + +type GenParser = Parsec Void ByteString + +data PackageInfo = PackageInfo + { pkgname :: String + , version :: Text + , homepage :: Text + , downloads :: [URI] + , checksums :: [Digest MD5] + } deriving (Eq, Show) + +variableEntry :: ByteString -> GenParser ByteString +variableEntry variable = string (Char8.append variable "=\"") + *> takeWhile1P Nothing (0x22 /=) + <* string "\"\n" + +variableSeparator :: GenParser () +variableSeparator = string " \\" *> space + +packageDownloads :: GenParser [URI] +packageDownloads = string "DOWNLOAD=\"" + *> sepBy parserBs variableSeparator + <* string "\"\n" + +hexDigit :: GenParser Word8 +hexDigit = + let digitPair = count 2 hexDigitChar + in fst . head . readHex . fmap (toEnum . fromIntegral) <$> digitPair + +packageChecksum :: GenParser ByteString +packageChecksum = ByteString.pack <$> count 16 hexDigit + +packageChecksums :: GenParser [ByteString] +packageChecksums = string "MD5SUM=\"" + *> sepBy packageChecksum variableSeparator + <* string "\"\n" + +parseInfoFile :: GenParser PackageInfo +parseInfoFile = PackageInfo + <$> (Char8.unpack <$> variableEntry "PKGNAM") + <*> (Text.decodeUtf8 <$> variableEntry "VERSION") + <*> (Text.decodeUtf8 <$> variableEntry "HOMEPAGE") + <*> packageDownloads + <*> (mapMaybe digestFromByteString <$> packageChecksums) + <* eof + +updateDownloadVersion :: PackageInfo -> Text -> Maybe String -> [URI] +updateDownloadVersion package toVersion gnomeVersion + = updateDownload (version package) toVersion gnomeVersion + <$> downloads package + +updateDownload :: Text -> Text -> Maybe String -> URI -> URI +updateDownload fromVersion toVersion gnomeVersion + = updateCoreVersion fromVersion toVersion gnomeVersion + . updatePackageVersion fromVersion toVersion gnomeVersion + +updatePackageVersion :: Text -> Text -> Maybe String -> URI -> URI +updatePackageVersion fromVersion toVersion _gnomeVersion download = download + { uriPath = uriPath download >>= traverse (traverse updatePathPiece) + } + where + updatePathPiece = mkPathPiece + . Text.replace fromMajor toMajor + . Text.replace fromVersion toVersion + . unRText + fromMajor = major fromVersion + toMajor = major toVersion + +major :: Text -> Text +major = Text.intercalate "." . take 2 . Text.splitOn "." + +updateCoreVersion :: Text -> Text -> Maybe String -> URI -> URI +updateCoreVersion _fromVersion _toVersion (Just gnomeVersion) download + | Just (False, pathPieces) <- uriPath download + , (beforeCore, afterCore) <- NonEmpty.break (comparePathPiece "core") pathPieces + , _ : _ : _ : sources : afterSources <- afterCore + , comparePathPiece "sources" sources && not (null afterSources) + , Right Authority{..} <- uriAuthority download + , ".gnome.org" `Text.isSuffixOf` unRText authHost + , Nothing <- authPort = + download { uriPath = buildPath beforeCore afterSources } + where + comparePathPiece this that = Just that == mkPathPiece this + buildPath beforeCore afterSources = do + core <- mkPathPiece "core" + let textGnomeVersion = Text.pack gnomeVersion + minorGnomeVersion <- mkPathPiece $ major textGnomeVersion + patchGnomeVersion <- mkPathPiece textGnomeVersion + sources <- mkPathPiece "sources" + let afterCore = core : minorGnomeVersion : patchGnomeVersion : sources : afterSources + (False,) <$> NonEmpty.nonEmpty (beforeCore ++ afterCore) +updateCoreVersion _ _ _ download = download + +update :: PackageInfo -> Text -> [URI] -> [Digest MD5] -> PackageInfo +update old toVersion downloads' checksums' = old + { version = toVersion + , downloads = downloads' + , checksums = checksums' + } + +generate :: PackageInfo -> Text +generate pkg = Lazy.Text.toStrict $ Text.Builder.toLazyText builder + where + digestToText = Text.pack . foldr hexAppender "" . ByteArray.unpack + hexAppender x acc + | x > 15 = showHex x acc + | otherwise = '0' : showHex x acc + builder = "PKGNAM=\"" <> Text.Builder.fromString (pkgname pkg) <> "\"\n" + <> "VERSION=\"" <> Text.Builder.fromText (version pkg) <> "\"\n" + <> "HOMEPAGE=\"" <> Text.Builder.fromText (homepage pkg) <> "\"\n" + <> generateMultiEntry "DOWNLOAD" (render <$> downloads pkg) + <> generateMultiEntry "MD5SUM" (digestToText <$> checksums pkg) + +generateMultiEntry :: Text -> [Text] -> Text.Builder +generateMultiEntry name entries = Text.Builder.fromText name + <> "=\"" + <> Text.Builder.fromText (Text.intercalate separator entries) + <> "\"\n" + where + padLength = Text.length name + 2 + separator = " \\\n" <> Text.replicate padLength " " diff --git a/lib/up2date.rb b/lib/up2date.rb index d9eab15..5d63da3 100644 --- a/lib/up2date.rb +++ b/lib/up2date.rb @@ -35,20 +35,6 @@ module SlackBuilder end end - # Request the latest version from the packagist API. - class Packagist < Repository - def initialize(vendor, name) - super() - - @vendor = vendor - @name = name - end - - def latest - `./bin/slackbuilder packagist #{@vendor} #{@name}`.strip - end - end - # Reads a remote LATEST file. class LatestText < Repository def initialize(latest_url) |
