27eca014be
9c4650358c
changed the Read instance for Key. I've checked all uses of that instance (by removing it and seeing what breaks), and they're all limited to the webapp, except one. That is GitAnnexDistribution's Read instance. So,9c4650358c
would have broken upgrades of git-annex from downloads.kitenet.net. Once the .info files there got updated for a new release, old releases would have failed to parse them and never upgraded. To fix this, I found a way to make the .info files that contain GitAnnexDistribution values be readable by the old version of git-annex. This commit was sponsored by Ewen McNeill.
207 lines
6.3 KiB
Haskell
207 lines
6.3 KiB
Haskell
{- 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).
|
|
-
|
|
- Also gpg signs the files.
|
|
-}
|
|
|
|
import Annex.Common
|
|
import Types.Distribution
|
|
import Build.Version (getChangelogVersion, Version)
|
|
import Utility.UserInfo
|
|
import Utility.Url
|
|
import Utility.Tmp
|
|
import Utility.FileSystemEncoding
|
|
import qualified Git.Construct
|
|
import qualified Annex
|
|
import Annex.Content
|
|
import Annex.WorkTree
|
|
import Git.Command
|
|
|
|
import Data.Time.Clock
|
|
import Data.Char
|
|
import System.Posix.Directory
|
|
|
|
-- 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 =
|
|
(map linuxarch ["i386", "amd64", "armel", "i386-ancient"]) ++
|
|
(map androidversion ["4.0", "4.3", "5.0"]) ++
|
|
[ (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")
|
|
, "git-annex/android/current/" ++ v ++ "/git-annex.apk"
|
|
)
|
|
autobuild f = "https://downloads.kitenet.net/git-annex/autobuild/" ++ f
|
|
|
|
main :: IO ()
|
|
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)
|
|
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
|
|
)
|
|
, oops $ "failed to download " ++ url
|
|
)
|
|
where
|
|
bvurl = takeDirectory url ++ "/build-version"
|
|
getbv = do
|
|
bv <- catchDefaultIO "" $ readProcess "curl" ["--silent", bvurl]
|
|
return $ if null bv || any (not . versionchar) bv then Nothing else Just bv
|
|
versionchar c = isAlphaNum c || c == '.' || c == '-'
|
|
|
|
makeinfos :: [(FilePath, Version)] -> Version -> Annex ()
|
|
makeinfos updated version = do
|
|
mapM_ (\f -> inRepo $ runBool [Param "annex", Param "add", File f]) (map fst updated)
|
|
void $ inRepo $ runBool
|
|
[ Param "commit"
|
|
, Param "-a"
|
|
, Param ("-S" ++ signingKey)
|
|
, Param "-m"
|
|
, Param $ "publishing git-annex " ++ version
|
|
]
|
|
now <- liftIO getCurrentTime
|
|
liftIO $ putStrLn $ "building info files"
|
|
forM_ updated $ \(f, bv) -> do
|
|
v <- lookupFile f
|
|
case v of
|
|
Nothing -> noop
|
|
Just k -> whenM (inAnnex k) $ do
|
|
liftIO $ putStrLn f
|
|
let infofile = f ++ ".info"
|
|
let d = GitAnnexDistribution
|
|
{ distributionUrl = mkUrl f
|
|
, distributionKey = k
|
|
, distributionVersion = bv
|
|
, distributionReleasedate = now
|
|
, distributionUrgentUpgrade = Nothing
|
|
}
|
|
liftIO $ writeFile infofile $ formatInfoFile d
|
|
void $ inRepo $ runBool [Param "add", File infofile]
|
|
signFile infofile
|
|
signFile f
|
|
void $ inRepo $ runBool
|
|
[ Param "commit"
|
|
, Param ("-S" ++ signingKey)
|
|
, Param "-m"
|
|
, Param $ "updated info files for git-annex " ++ version
|
|
]
|
|
void $ inRepo $ runBool
|
|
[ Param "annex"
|
|
, Param "move"
|
|
, Param "--to"
|
|
, Param "website"
|
|
]
|
|
void $ inRepo $ runBool
|
|
[ Param "annex"
|
|
, Param "sync"
|
|
]
|
|
|
|
-- Check for out of date info files.
|
|
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
|
|
|
|
getRepoDir :: IO FilePath
|
|
getRepoDir = do
|
|
home <- liftIO myHomeDir
|
|
return $ home </> "lib" </> "downloads"
|
|
|
|
mkUrl :: FilePath -> String
|
|
mkUrl f = "https://downloads.kitenet.net/" ++ f
|
|
|
|
signFile :: FilePath -> Annex ()
|
|
signFile f = do
|
|
void $ liftIO $ boolSystem "gpg"
|
|
[ Param "-a"
|
|
, Param $ "--default-key=" ++ signingKey
|
|
, Param "--detach-sign"
|
|
, 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'
|