forked from NixOS/nixpkgs
-
Notifications
You must be signed in to change notification settings - Fork 0
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
[WIP] Parallel GH actions workflow for Nixpkgs eval
Partly taken from NixOS#352808 and NixOS#269403
- Loading branch information
Showing
3 changed files
with
175 additions
and
0 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,53 @@ | ||
name: Eval | ||
|
||
on: pull_request_target | ||
|
||
permissions: | ||
contents: read | ||
|
||
jobs: | ||
tests: | ||
name: eval-check | ||
runs-on: ubuntu-latest | ||
strategy: | ||
matrix: | ||
system: [x86_64-linux, aarch64-linux, aarch64-darwin, x86_64-darwin] | ||
steps: | ||
# Important: Because of `pull_request_target`, this doesn't check out the PR, | ||
# but rather the base branch of the PR, which is needed so we don't run untrusted code | ||
- uses: actions/checkout@11bd71901bbe5b1630ceea73d27597364c9af683 # v4.2.2 | ||
with: | ||
path: base | ||
sparse-checkout: ci | ||
- name: Resolving the merge commit | ||
env: | ||
GH_TOKEN: ${{ github.token }} | ||
run: | | ||
if mergedSha=$(base/ci/get-merge-commit.sh ${{ github.repository }} ${{ github.event.number }}); then | ||
echo "Checking the merge commit $mergedSha" | ||
echo "mergedSha=$mergedSha" >> "$GITHUB_ENV" | ||
else | ||
# Skipping so that no notifications are sent | ||
echo "Skipping the rest..." | ||
fi | ||
rm -rf base | ||
- uses: actions/checkout@11bd71901bbe5b1630ceea73d27597364c9af683 # v4.2.2 | ||
# Add this to _all_ subsequent steps to skip them | ||
if: env.mergedSha | ||
with: | ||
ref: ${{ env.mergedSha }} | ||
|
||
- uses: cachix/install-nix-action@08dcb3a5e62fa31e2da3d490afc4176ef55ecd72 # v30 | ||
if: env.mergedSha | ||
|
||
- name: Enable swap | ||
if: env.mergedSha | ||
run: | | ||
sudo fallocate -l 10G /swapfile | ||
sudo chmod 600 /swapfile | ||
sudo mkswap /swapfile | ||
sudo swapon /swapfile | ||
- name: Check eval | ||
if: env.mergedSha | ||
run: ./ci/eval-nixpkgs.sh --system "${{ matrix.system }}" |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,61 @@ | ||
#!/usr/bin/env nix-shell | ||
#!nix-shell -i bash -p moreutils -I nixpkgs=channel:nixpkgs-unstable | ||
|
||
set -euxo pipefail | ||
|
||
system="x86_64-linux" | ||
SCRIPT_DIR="$( cd "$( dirname "${BASH_SOURCE[0]}" )" &> /dev/null && pwd )" | ||
NIXPKGS_PATH="$(readlink -f "$SCRIPT_DIR"/..)" | ||
|
||
parseArgs() { | ||
while [[ $# -gt 0 ]]; do | ||
case $1 in | ||
--system) | ||
system=$2 | ||
shift 2 | ||
;; | ||
*) | ||
echo "Unknown argument: $1" | ||
exit 1 | ||
;; | ||
esac | ||
done | ||
} | ||
|
||
main() { | ||
parseArgs "$@" | ||
tmpdir=$(mktemp -d) | ||
trap 'rm -rf "$tmpdir"' EXIT | ||
|
||
nix-instantiate --eval --strict --json --arg enableWarnings false "$NIXPKGS_PATH"/pkgs/top-level/release-attrpaths-superset.nix -A paths > "$tmpdir/paths.json" | ||
|
||
CORES=$(nproc) | ||
# Originally @amjoseph: note that the number of processes spawned is four times | ||
# the number of cores -- this helps in two ways: | ||
# 1. Keeping cores busy while I/O operations are in flight | ||
# 2. Since the amount of time needed for the jobs is *not* balanced | ||
# this minimizes the "tail latency" for the very last job to finish | ||
# (on one core) by making the job size smaller. | ||
NUM_CHUNKS=$(( 4 * CORES )) | ||
|
||
|
||
( | ||
set +e | ||
parallel -j "$CORES" \ | ||
nix-env -qaP --no-name --out-path --arg checkMeta true --arg includeBroken true \ | ||
--arg systems "[\"$system\"]" \ | ||
-f "$NIXPKGS_PATH"/ci/parallel.nix --arg attrPathFile "$tmpdir"/paths.json \ | ||
--arg numChunks "$NUM_CHUNKS" --show-trace --arg myChunk \ | ||
-- $(seq 0 $(( NUM_CHUNKS - 1 ))) > "$tmpdir/paths" | ||
echo $? > "$tmpdir/exit-code" | ||
) & | ||
pid=$! | ||
while kill -0 "$pid"; do | ||
free -g >&2 | ||
sleep 20 | ||
done | ||
jq --raw-input --slurp 'split("\n") | map(select(. != "") | split(" ") | map(select(. != "")) | { key: .[0], value: .[1] }) | from_entries' "$tmpdir/paths" | ||
exit "$(cat "$tmpdir/exit-code")" | ||
} | ||
|
||
main "$@" |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,61 @@ | ||
/* | ||
Invocation: | ||
Invocation; note that the number of processes spawned is four times | ||
the number of cores -- this helps in two ways: | ||
1. Keeping cores busy while I/O operations are in flight | ||
2. Since the amount of time needed for the jobs is *not* balanced | ||
this minimizes the "tail latency" for the very last job to finish | ||
(on one core) by making the job size smaller. | ||
*/ | ||
# see pkgs/top-level/nohydra | ||
{ lib ? import ../lib | ||
, checkMeta | ||
, includeBroken ? true | ||
, path ? ./.. | ||
, systems | ||
, myChunk | ||
, numChunks | ||
, attrPathFile | ||
}: | ||
|
||
let | ||
attrPaths = builtins.fromJSON (builtins.readFile attrPathFile); | ||
chunkSize = (lib.length attrPaths) / numChunks; | ||
myPaths = | ||
let | ||
dropped = lib.drop (chunkSize*myChunk) attrPaths; | ||
in | ||
if myChunk == numChunks - 1 | ||
then dropped | ||
else lib.take chunkSize dropped; | ||
|
||
unfiltered = import ../pkgs/top-level/release-outpaths.nix { | ||
inherit checkMeta path includeBroken systems; | ||
}; | ||
|
||
f = i: m: a: | ||
lib.mapAttrs (name: values: | ||
if a ? ${name} then | ||
if lib.any (value: lib.length value <= i + 1) values then | ||
a.${name} | ||
else | ||
f (i + 1) values a.${name} | ||
else | ||
null | ||
) (lib.groupBy (a: lib.elemAt a i) m); | ||
|
||
filtered = f 0 myPaths unfiltered; | ||
|
||
recurseEverywhere = val: | ||
if lib.isDerivation val || !(lib.isAttrs val) | ||
then val | ||
else (builtins.mapAttrs (_: v: recurseEverywhere v) val) | ||
// { recurseForDerivations = true; }; | ||
|
||
in | ||
recurseEverywhere filtered |