git-annex/Build/DistributionUpdate.hs

208 lines
6.3 KiB
Haskell
Raw Normal View History

{- Downloads git-annex autobuilds and installs them into the git-annex
- repository in ~/lib/downloads that is used to distribute git-annex
- releases.
-
- Generates info files, containing the version (of the corresponding file
- from the autobuild).
2014-04-21 15:24:34 +00:00
-
- Also gpg signs the files.
-}
2013-11-22 16:21:53 +00:00
import Annex.Common
2013-11-22 16:21:53 +00:00
import Types.Distribution
2015-04-20 20:09:24 +00:00
import Build.Version (getChangelogVersion, Version)
2013-11-22 16:21:53 +00:00
import Utility.UserInfo
import Utility.Url
2018-01-15 16:18:00 +00:00
import Utility.Tmp.Dir
import Utility.FileSystemEncoding
2013-11-22 16:21:53 +00:00
import qualified Git.Construct
import qualified Annex
import Annex.Content
2016-01-14 19:55:37 +00:00
import Annex.WorkTree
2013-11-22 16:21:53 +00:00
import Git.Command
import Data.Time.Clock
2014-06-18 20:11:20 +00:00
import Data.Char
import System.Posix.Directory
2013-11-22 16:21:53 +00:00
2014-04-21 15:24:34 +00:00
-- git-annex distribution signing key (for Joey Hess)
signingKey :: String
signingKey = "89C809CB"
-- URL to an autobuilt git-annex file, and the place to install
-- it in the repository.
autobuilds :: [(URLString, FilePath)]
autobuilds =
2015-09-23 17:36:45 +00:00
(map linuxarch ["i386", "amd64", "armel", "i386-ancient"]) ++
2015-01-08 23:29:17 +00:00
(map androidversion ["4.0", "4.3", "5.0"]) ++
2014-11-11 20:18:51 +00:00
[ (autobuild "x86_64-apple-yosemite/git-annex.dmg", "git-annex/OSX/current/10.10_Yosemite/git-annex.dmg")
, (autobuild "windows/git-annex-installer.exe", "git-annex/windows/current/git-annex-installer.exe")
]
where
linuxarch a =
( autobuild (a ++ "/git-annex-standalone-" ++ a ++ ".tar.gz")
, "git-annex/linux/current/git-annex-standalone-" ++ a ++ ".tar.gz"
)
androidversion v =
( autobuild ("android/" ++ v ++ "/git-annex.apk")
2014-06-18 19:49:43 +00:00
, "git-annex/android/current/" ++ v ++ "/git-annex.apk"
)
2014-07-07 18:30:38 +00:00
autobuild f = "https://downloads.kitenet.net/git-annex/autobuild/" ++ f
main :: IO ()
2013-11-22 16:21:53 +00:00
main = do
useFileSystemEncoding
version <- liftIO getChangelogVersion
repodir <- getRepoDir
changeWorkingDirectory repodir
updated <- catMaybes <$> mapM (getbuild repodir) autobuilds
state <- Annex.new =<< Git.Construct.fromPath "."
Annex.eval state (makeinfos updated version)
-- Download a build from the autobuilder, virus check it, and return its
-- version.
-- It's very important that the version matches the build, otherwise
-- auto-upgrades can loop reatedly. So, check build-version before
-- and after downloading the file.
getbuild :: FilePath -> (URLString, FilePath) -> IO (Maybe (FilePath, Version))
getbuild repodir (url, f) = do
bv1 <- getbv
let dest = repodir </> f
let tmp = dest ++ ".tmp"
nukeFile tmp
createDirectoryIfMissing True (parentDir dest)
2014-06-18 19:44:16 +00:00
let oops s = do
nukeFile tmp
putStrLn $ "*** " ++ s
return Nothing
ifM (download url tmp def)
( ifM (liftIO $ virusFree tmp)
( do
bv2 <- getbv
case bv2 of
Nothing -> oops $ "no build-version file for " ++ url
(Just v)
| bv2 == bv1 -> do
nukeFile dest
renameFile tmp dest
-- remove git rev part of version
let v' = takeWhile (/= '-') v
return $ Just (f, v')
| otherwise -> oops $ "build version changed while downloading " ++ url ++ " " ++ show (bv1, bv2)
, oops $ "VIRUS detected in " ++ url
)
2014-06-18 19:44:16 +00:00
, oops $ "failed to download " ++ url
)
where
2014-06-18 20:24:46 +00:00
bvurl = takeDirectory url ++ "/build-version"
getbv = do
2014-06-18 20:24:46 +00:00
bv <- catchDefaultIO "" $ readProcess "curl" ["--silent", bvurl]
2014-06-18 20:11:20 +00:00
return $ if null bv || any (not . versionchar) bv then Nothing else Just bv
versionchar c = isAlphaNum c || c == '.' || c == '-'
2013-11-22 16:21:53 +00:00
makeinfos :: [(FilePath, Version)] -> Version -> Annex ()
makeinfos updated version = do
2014-11-11 20:49:24 +00:00
mapM_ (\f -> inRepo $ runBool [Param "annex", Param "add", File f]) (map fst updated)
void $ inRepo $ runBool
[ Param "commit"
2014-02-27 16:20:53 +00:00
, Param "-a"
2015-04-06 22:56:38 +00:00
, Param ("-S" ++ signingKey)
, Param "-m"
, Param $ "publishing git-annex " ++ version
]
2013-11-22 16:21:53 +00:00
now <- liftIO getCurrentTime
liftIO $ putStrLn $ "building info files"
forM_ updated $ \(f, bv) -> do
v <- lookupFile f
2013-11-22 16:21:53 +00:00
case v of
Nothing -> noop
Just k -> whenM (inAnnex k) $ do
2013-11-22 16:21:53 +00:00
liftIO $ putStrLn f
let infofile = f ++ ".info"
let d = GitAnnexDistribution
{ distributionUrl = mkUrl f
2013-11-22 16:21:53 +00:00
, distributionKey = k
, distributionVersion = bv
2013-11-22 16:21:53 +00:00
, distributionReleasedate = now
, distributionUrgentUpgrade = Nothing
}
liftIO $ writeFile infofile $ formatInfoFile d
2014-04-21 15:24:34 +00:00
void $ inRepo $ runBool [Param "add", File infofile]
signFile infofile
signFile f
2013-11-22 16:21:53 +00:00
void $ inRepo $ runBool
[ Param "commit"
2015-04-06 22:56:38 +00:00
, Param ("-S" ++ signingKey)
2015-04-06 22:38:34 +00:00
, Param "-m"
, Param $ "updated info files for git-annex " ++ version
2013-11-22 16:21:53 +00:00
]
2013-11-22 19:02:31 +00:00
void $ inRepo $ runBool
2013-11-25 18:14:45 +00:00
[ Param "annex"
, Param "move"
, Param "--to"
, Param "website"
2013-11-22 19:02:31 +00:00
]
void $ inRepo $ runBool
2013-11-25 18:14:45 +00:00
[ Param "annex"
, Param "sync"
2013-11-22 19:02:31 +00:00
]
-- Check for out of date info files.
2014-02-10 19:33:37 +00:00
infos <- liftIO $ filter (".info" `isSuffixOf`)
<$> dirContentsRecursive "git-annex"
ds <- liftIO $ forM infos (readish <$$> readFile)
let dis = zip infos ds
let ood = filter outofdate dis
unless (null ood) $
error $ "Some info files are out of date: " ++ show (map fst ood)
where
outofdate (_, md) = case md of
Nothing -> True
Just d -> distributionVersion d /= version
2013-11-22 16:21:53 +00:00
getRepoDir :: IO FilePath
getRepoDir = do
home <- liftIO myHomeDir
return $ home </> "lib" </> "downloads"
mkUrl :: FilePath -> String
mkUrl f = "https://downloads.kitenet.net/" ++ f
2014-04-21 15:24:34 +00:00
signFile :: FilePath -> Annex ()
signFile f = do
void $ liftIO $ boolSystem "gpg"
[ Param "-a"
, Param $ "--default-key=" ++ signingKey
2014-04-21 15:56:06 +00:00
, Param "--detach-sign"
2014-04-21 15:24:34 +00:00
, File f
]
liftIO $ rename (f ++ ".asc") (f ++ ".sig")
void $ inRepo $ runBool [Param "add", File (f ++ ".sig")]
-- clamscan should handle unpacking archives, but did not in my
-- testing, so do it manually.
virusFree :: FilePath -> IO Bool
virusFree f
| ".tar.gz" `isSuffixOf` f = unpack $ \tmpdir ->
boolSystem "tar" [ Param "xf", File f, Param "-C", File tmpdir ]
| ".dmg" `isSuffixOf` f = unpack $ \tmpdir -> do
-- 7z can extract partitions from a dmg, and then
-- run on partitions can extract their files
unhfs tmpdir f
parts <- filter (".hfs" `isSuffixOf`) <$> getDirectoryContents tmpdir
forM_ parts $ unhfs tmpdir
return True
| otherwise = clamscan f
where
clamscan f' = boolSystem "clamscan"
[ Param "--no-summary"
, Param "-r"
, Param f'
]
unpack unpacker = withTmpDir "clamscan" $ \tmpdir -> do
unlessM (unpacker tmpdir) $
error $ "Failed to unpack " ++ f ++ " for virus scan"
clamscan tmpdir
unhfs dest f' = unlessM (boolSystem "7z" [ Param "x", Param ("-o" ++ dest), File f' ]) $
error $ "Failed extracting hfs " ++ f'