Skip to content
Snippets Groups Projects
Commit 1f221ba8 authored by Ben Gamari's avatar Ben Gamari :turtle:
Browse files

ci: Support splitting build across multiple jobs

This utilizes GitLab CI's `parallel`[1] field to divide the build into
several jobs, each handling a subset of the built packages. The job
count is a bit of a trade-off between build re-use and parallelism. I'm
trying 5 for now.

[1] https://docs.gitlab.com/ee/ci/yaml/#parallel
parent 37cbfd7f
No related branches found
No related tags found
1 merge request!51ci: Support splitting build across multiple jobs
...@@ -14,6 +14,7 @@ ...@@ -14,6 +14,7 @@
# #
stages: stages:
- prebuild
- test - test
- update-repo - update-repo
- deploy - deploy
...@@ -72,19 +73,11 @@ build-8.8: ...@@ -72,19 +73,11 @@ build-8.8:
- branches - branches
- merge_requests - merge_requests
.build: .base:
stage: test
tags: tags:
- x86_64-linux - x86_64-linux
image: nixos/nix image: nixos/nix
cache:
key: build-HEAD
paths:
- store.nar
before_script: before_script:
- | - |
if [ -e store.nar ]; then if [ -e store.nar ]; then
...@@ -99,29 +92,52 @@ build-8.8: ...@@ -99,29 +92,52 @@ build-8.8:
- echo "Bindist tarball is $GHC_TARBALL" - echo "Bindist tarball is $GHC_TARBALL"
- | - |
nix build \ nix build \
-f https://github.com/mpickering/ghc-artefact-nix/archive/master.tar.gz \ -f https://github.com/mpickering/ghc-artefact-nix/archive/master.tar.gz \
--argstr url $GHC_TARBALL \ --argstr url $GHC_TARBALL \
--out-link ghc \ --out-link ghc \
ghcHEAD ghcHEAD
- export GHC=`pwd`/ghc/bin/ghc
- rm -Rf $HOME/.cabal/packages/local
- export GHC=`pwd`/ghc/bin/ghc - export GHC=`pwd`/ghc/bin/ghc
- rm -Rf $HOME/.cabal/packages/local ci/run
# Build CI executable # Build CI executable
- | - |
nix-build ./ci -j$CPUS --no-build-output nix-build ./ci -j$CPUS --no-build-output
nix-store --export \ nix-store --export \
$(nix-store -qR --include-outputs \ $(nix-store -qR $(nix-instantiate --quiet ./ci)) \
$(nix-instantiate --quiet ./ci)) \ > store.nar
> store.nar
# Test it # Test it
- nix run -f ./ci -c run-ci - nix run -f ./ci -c run-ci $ARGS
prebuild:
stage: prebuild
extends: .base
only:
- branches
- merge_requests
variables:
ARGS: "--only=acme-box"
artifacts:
paths:
- store.nar
- ci/run
expire_in: 1 day
cache:
key: build-HEAD
paths:
- store.nar
.build:
stage: test
extends: .base
dependencies:
- prebuild
parallel: 5
after_script: after_script:
- ls -lh - ls -lh
- | - |
nix run -f ./ci -c \ nix run -f ./ci -c \
tar -cJf results.tar.xz -C ci/run \ tar -cJf results.tar.xz -C ci/run \
results.json logs results.json logs
artifacts: artifacts:
when: always when: always
paths: paths:
......
...@@ -53,6 +53,12 @@ newtype BrokenPackages = BrokenPackages { getBrokenPackageNames :: S.Set PkgName ...@@ -53,6 +53,12 @@ newtype BrokenPackages = BrokenPackages { getBrokenPackageNames :: S.Set PkgName
failureExpected :: BrokenPackages -> PkgName -> Bool failureExpected :: BrokenPackages -> PkgName -> Bool
failureExpected (BrokenPackages pkgs) name = name `S.member` pkgs failureExpected (BrokenPackages pkgs) name = name `S.member` pkgs
-- | To facilitate splitting the builds across multiple GitLab CI jobs we
-- support *build partitioning*, where the tested packages are split evenly
-- into @n@ partitions. A 'BuildPartition' identifies one set in such a
-- partitioning.
data BuildPartition = BuildPartition { bpIndex, bpCount :: !Int }
data Config = Config { configPatchDir :: FilePath data Config = Config { configPatchDir :: FilePath
, configCompiler :: FilePath , configCompiler :: FilePath
, configGhcOptions :: [String] , configGhcOptions :: [String]
...@@ -62,6 +68,7 @@ data Config = Config { configPatchDir :: FilePath ...@@ -62,6 +68,7 @@ data Config = Config { configPatchDir :: FilePath
, configExtraCabalFragments :: [FilePath] , configExtraCabalFragments :: [FilePath]
, configExtraPackages :: [(Cabal.PackageName, Version)] , configExtraPackages :: [(Cabal.PackageName, Version)]
, configExpectedBrokenPkgs :: BrokenPackages , configExpectedBrokenPkgs :: BrokenPackages
, configBuildPartition :: Maybe BuildPartition
} }
cabalOptions :: Config -> [String] cabalOptions :: Config -> [String]
...@@ -82,6 +89,7 @@ config = ...@@ -82,6 +89,7 @@ config =
<*> extraCabalFragments <*> extraCabalFragments
<*> extraPackages <*> extraPackages
<*> expectedBrokenPkgs <*> expectedBrokenPkgs
<*> optional buildPartition
where where
patchDir = option str (short 'p' <> long "patches" <> help "patch directory" <> value "./patches") patchDir = option str (short 'p' <> long "patches" <> help "patch directory" <> value "./patches")
compiler = option str (short 'w' <> long "with-compiler" <> help "path of compiler") compiler = option str (short 'w' <> long "with-compiler" <> help "path of compiler")
...@@ -98,6 +106,13 @@ config = ...@@ -98,6 +106,13 @@ config =
$ option $ option
(fmap toPkgName pkgName) (fmap toPkgName pkgName)
(short 'b' <> long "expect-broken" <> metavar "PKGNAME" <> help "expect the given package to fail to build") (short 'b' <> long "expect-broken" <> metavar "PKGNAME" <> help "expect the given package to fail to build")
buildPartition =
f <$> option auto (long "partition" <> metavar "N" <> help "the build partition index")
<*> option auto (long "partition-count" <> metavar "N" <> help "the number of build partitions")
where
f i n
| i < n = BuildPartition i n
| otherwise = error "partition index must be less than partition count"
pkgVer :: ReadM (Cabal.PackageName, Version) pkgVer :: ReadM (Cabal.PackageName, Version)
pkgVer = str >>= parse . T.pack pkgVer = str >>= parse . T.pack
...@@ -115,11 +130,18 @@ config = ...@@ -115,11 +130,18 @@ config =
pkgName :: ReadM Cabal.PackageName pkgName :: ReadM Cabal.PackageName
pkgName = str >>= maybe (fail "invalid package name") pure . simpleParse pkgName = str >>= maybe (fail "invalid package name") pure . simpleParse
takeBuildPartition :: BuildPartition -> [a] -> [a]
takeBuildPartition (BuildPartition i n) xs =
take partSize $ drop (i * partSize) xs
where
partSize = length xs `div` n + 1
testPatches :: Config -> IO () testPatches :: Config -> IO ()
testPatches cfg = do testPatches cfg = do
setup cfg setup cfg
packages <- findPatchedPackages (configPatchDir cfg) packages <- findPatchedPackages (configPatchDir cfg)
packages <- return (packages ++ configExtraPackages cfg) packages <- return $ maybe id takeBuildPartition (configBuildPartition cfg)
$ (packages ++ configExtraPackages cfg)
let packages' :: S.Set (Cabal.PackageName, Version) let packages' :: S.Set (Cabal.PackageName, Version)
packages' packages'
| Just only <- configOnlyPackages cfg | Just only <- configOnlyPackages cfg
......
...@@ -26,6 +26,10 @@ if [ -n "$EXTRA_HC_OPTS" ]; then ...@@ -26,6 +26,10 @@ if [ -n "$EXTRA_HC_OPTS" ]; then
EXTRA_OPTS="$EXTRA_OPTS --ghc-option=\"$EXTRA_HC_OPTS\"" EXTRA_OPTS="$EXTRA_OPTS --ghc-option=\"$EXTRA_HC_OPTS\""
fi fi
if [ -n "$CI_NODE_INDEX" ]; then
EXTRA_OPTS="$EXTRA_OPTS --partition=$[$CI_NODE_INDEX-1] --partition-count=$CI_NODE_TOTAL"
fi
mkdir -p run mkdir -p run
echo "" > run/deps.cabal.project echo "" > run/deps.cabal.project
......
0% Loading or .
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment