Skip to content

Commit bd2aabe

Browse files
committed
feat(ci): scoped rebuilds
All hosts imports are dynamically gotten and compared against the changed files to know if they actually need building or not.
1 parent c8790c9 commit bd2aabe

File tree

7 files changed

+231
-28
lines changed

7 files changed

+231
-28
lines changed

.github/workflows/ci.yaml

Lines changed: 131 additions & 26 deletions
Original file line numberDiff line numberDiff line change
@@ -12,6 +12,126 @@ on:
1212
type: string
1313

1414
jobs:
15+
determine-affected:
16+
runs-on: ubuntu-latest
17+
outputs:
18+
matrix: ${{ steps.set-matrix.outputs.matrix }}
19+
all-hosts: ${{ steps.set-matrix.outputs.all-hosts }}
20+
build-all: ${{ steps.set-matrix.outputs.build-all }}
21+
steps:
22+
- name: Checkout repository
23+
uses: actions/checkout@v4
24+
with:
25+
ref: ${{ inputs.branch }}
26+
fetch-depth: 0
27+
28+
- uses: ./.github/actions/setup-nix
29+
with:
30+
arch: x86_64-linux
31+
TS_OAUTH_CLIENT_ID: ${{ secrets.TS_OAUTH_CLIENT_ID }}
32+
TS_OAUTH_SECRET: ${{ secrets.TS_OAUTH_SECRET }}
33+
ATTIC_ENDPOINT: ${{ secrets.ATTIC_ENDPOINT }}
34+
ATTIC_CACHE: ${{ secrets.ATTIC_CACHE }}
35+
ATTIC_TOKEN: ${{ secrets.ATTIC_TOKEN }}
36+
37+
- name: Get hosts from flake
38+
id: get-hosts
39+
run: |
40+
ALL_HOSTS=$(nix flake show --impure --json | jq -r '.nixosConfigurations | keys | .[]' | jq -R . | jq -s .)
41+
echo "All hosts from flake: $ALL_HOSTS"
42+
echo "all-hosts=$ALL_HOSTS" >> "$GITHUB_OUTPUT"
43+
44+
# Special Properties
45+
NO_BUILD=()
46+
EXTRA_SPACE=("nixmi" "winix")
47+
48+
deviceTypes=$(find hosts -maxdepth 1 -type d -name '*' | grep -oP 'hosts/\K[^/]+' | grep -v shared)
49+
echo "host-types=$(echo "$deviceTypes" | jq -R . | jq -s .)" >> "$GITHUB_OUTPUT"
50+
51+
users_imports="{}"
52+
declare -a host_entries
53+
for host in $(echo "$ALL_HOSTS" | jq -r '.[]'); do
54+
noBuild=false
55+
if [[ " ${NO_BUILD[*]} " = *" $host "* ]]; then
56+
noBuild=true
57+
fi
58+
extraSpace=false
59+
if [[ " ${EXTRA_SPACE[*]} " = *" $host "* ]]; then
60+
extraSpace=true
61+
fi
62+
63+
hostType="unknown"
64+
for deviceType in $deviceTypes; do
65+
if [ -d "hosts/$deviceType/$host" ]; then
66+
hostType="$deviceType"
67+
break
68+
fi
69+
done
70+
71+
imports=$(./utils/get-imports.nu "OS" "$host" | jq -c .)
72+
users=$(nix eval --impure --json .#nixosConfigurations.nixmi.config.home-manager.users --apply builtins.attrNames | jq -c .)
73+
for user in $(echo "$users" | jq -r '.[]'); do
74+
if [[ "$(echo "$users_imports" | jq -r ".$user == null")" == "true" ]]; then
75+
users_imports=$(echo "$users_imports" | jq \
76+
--arg username "$user" \
77+
--argjson user "$(./utils/get-imports.nu "HOME" "$user" | jq -c .)" \
78+
'.[$username] = $user')
79+
fi
80+
81+
imports=$(jq -s '.[0] + .[1]' <(echo "$imports") <(echo "$users_imports" | jq -c ".$user"))
82+
done
83+
84+
entry=$(jq -n \
85+
--arg host "$host" \
86+
--arg system "x86_64-linux" \
87+
--arg hostType "$hostType" \
88+
--argjson users "$users" \
89+
--argjson imports "$imports" \
90+
--argjson extraSpace "$extraSpace" \
91+
--argjson noBuild "$noBuild" \
92+
'{host: $host, system: $system, hostType: $hostType, users: $users, imports: $imports, extraSpace: $extraSpace, noBuild: $noBuild}')
93+
94+
host_entries+=("$entry")
95+
done
96+
97+
HOST_DETAILS=$(jq -s 'map({key: .host, value: .}) | from_entries' <<<"${host_entries[@]}")
98+
echo "Host details: $HOST_DETAILS"
99+
echo "host-details=$HOST_DETAILS" >> "$GITHUB_OUTPUT"
100+
101+
- name: Determine affected hosts
102+
id: set-matrix
103+
run: |
104+
CHANGED_FILES=$(git diff --name-only ${{ github.event.before }} ${{ github.sha }} | grep -v '^D' || true)
105+
echo "Changed files:"
106+
echo "$CHANGED_FILES"
107+
108+
HOST_DETAILS=$(echo "${{steps.get-hosts.outputs.host-details}}" | jq -c .)
109+
110+
declare -a build_hosts
111+
if echo "$CHANGED_FILES" | grep -qE '(^flake\.nix$|^flake\.lock$|^lib/|^overlays/|^pkgs/)'; then
112+
echo "Found changes in core files. All hosts will be rebuilt."
113+
build_hosts=("$(echo "$HOST_DETAILS" | jq -r 'keys[]')")
114+
else
115+
rebuild_json=$(jq --argjson changed "$(jq -R -s -c . <<<"$CHANGED_FILES")" '
116+
. as $hosts |
117+
to_entries
118+
| map(select(.value.imports |
119+
any( . as $imp | ($changed | index($imp)) )))
120+
| map(.key)
121+
' <<<"$HOST_DETAILS")
122+
123+
mapfile -t build_hosts < <(jq -r '.[]' <<<"$rebuild_json")
124+
fi
125+
126+
declare -a matrix_entries
127+
for host in "${build_hosts[@]}"; do
128+
entry=$(echo "$HOST_DETAILS" | jq -r ".$host")
129+
matrix_entries+=("$entry")
130+
done
131+
132+
MATRIX_JSON="$(printf '%s\n' "${matrix_entries[@]}" | jq -s .)"
133+
echo "matrix=$MATRIX_JSON" >> "$GITHUB_OUTPUT"
134+
15135
checks:
16136
runs-on: ubuntu-latest
17137
steps:
@@ -24,34 +144,16 @@ jobs:
24144
uses: DeterminateSystems/flake-checker-action@main
25145

26146
build:
147+
needs: determine-affected
148+
if: needs.determine-affected.outputs.matrix != '[]'
27149
runs-on: ubuntu-latest
28150
concurrency:
29151
group: ${{ github.workflow }}-${{ github.ref }}-${{ matrix.host }}-${{ matrix.system }}
30152
cancel-in-progress: true
31153
strategy:
32154
fail-fast: false
33-
# TODO - Use a step to determine the hosts dynamically from the flake
34155
matrix:
35-
include:
36-
- host: nixmi
37-
system: x86_64-linux
38-
extraSpace: true
39-
- host: winix
40-
system: x86_64-linux
41-
- host: nixai
42-
system: x86_64-linux
43-
- host: nixarr
44-
system: x86_64-linux
45-
- host: nixcloud
46-
system: x86_64-linux
47-
- host: nixdev
48-
system: x86_64-linux
49-
- host: nixio
50-
system: x86_64-linux
51-
- host: nixmon
52-
system: x86_64-linux
53-
- host: nixserv
54-
system: x86_64-linux
156+
include: ${{ fromJSON(needs.determine-affected.outputs.matrix) }}
55157

56158
name: ${{ matrix.host }} on ${{ matrix.system }}
57159

@@ -62,16 +164,18 @@ jobs:
62164
ref: ${{ inputs.branch }}
63165

64166
- name: Free Disk Space
65-
if: ${{ matrix.extraSpace == true && !env.ACT }}
167+
if: ${{ matrix.extraSpace && !env.ACT }}
66168
uses: jlumbroso/free-disk-space@main
67169
with:
68170
swap-storage: true
69171
tool-cache: true
172+
70173
- name: Create Dir for Mounting more Disk Space ❄
71-
if: ${{ matrix.extraSpace == true && !env.ACT }}
174+
if: ${{ matrix.extraSpace && !env.ACT }}
72175
run: sudo mkdir /nix
176+
73177
- name: Maximize Disk Space
74-
if: ${{ matrix.extraSpace == true && !env.ACT }}
178+
if: ${{ matrix.extraSpace && !env.ACT }}
75179
uses: easimon/maximize-build-space@v10
76180
with:
77181
build-mount-path: /nix
@@ -83,8 +187,9 @@ jobs:
83187
temp-reserve-mb: 100
84188
swap-size-mb: 4096
85189
root-reserve-mb: 1024
190+
86191
- name: Ensure correct permissions for /nix
87-
if: ${{ matrix.extraSpace == true && !env.ACT }}
192+
if: ${{ matrix.extraSpace && !env.ACT }}
88193
run: sudo chown -R root:root /nix
89194

90195
- uses: ./.github/actions/setup-nix
@@ -102,7 +207,7 @@ jobs:
102207
run: nix eval --impure --accept-flake-config .#nixosConfigurations.${{ matrix.host }}.config.system.build.toplevel
103208

104209
- name: Build ${{ matrix.host }}
105-
# if: matrix.noBuild != true
210+
if: ${{ !matrix.noBuild }}
106211
env:
107212
_system: ${{ matrix.system }}
108213
run: nix build --impure --accept-flake-config .#nixosConfigurations.${{ matrix.host }}.config.system.build.toplevel

flake.nix

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -161,7 +161,7 @@
161161
};
162162
};
163163

164-
users = lib.genAttrs [ "racci" ] (name: builders.home.mkHomeManager system name { });
164+
users = lib.genAttrs [ "racci" "root" ] (name: builders.home.mkHomeManager system name { });
165165
in
166166
{
167167
nixosConfigurations = builtins.mapAttrs (_n: v: v.system) configurations;

lib/builders/home/userConf.nix

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,5 @@
11
{
2+
flake,
23
lib,
34
name,
45
userDirectory,
@@ -12,6 +13,7 @@
1213
homeDirectory = lib.mkDefault "/home/${name}";
1314

1415
sessionPath = [ "$HOME/.local/bin" ];
16+
stateVersion = "25.05";
1517
};
1618

1719
sops = lib.mkIf (user != null) {
@@ -21,6 +23,7 @@
2123

2224
imports =
2325
[
26+
"${flake}/home/shared/global"
2427
"${userDirectory}/global.nix"
2528
]
2629
++ (

lib/builders/system/mkRaw.nix

Lines changed: 0 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -38,7 +38,6 @@ rec {
3838
home-manager = {
3939
useUserPackages = true;
4040
useGlobalPkgs = true;
41-
sharedModules = [ "${flake}/home/shared/global" ];
4241
};
4342

4443
system.stateVersion = "25.05";

utils/get-hm-imports.nix

Lines changed: 37 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,37 @@
1+
home:
2+
let
3+
inherit (home) pkgs;
4+
inherit (pkgs) lib;
5+
6+
# Doing this to silence the warning about collectModules being an internal function
7+
inherit
8+
(
9+
(import (home.options._module.specialArgs.value.inputs.nixpkgs + "/lib/modules.nix") {
10+
lib = lib.extend (
11+
_: prev: {
12+
trivial = prev.trivial // {
13+
warn = _: y: y;
14+
};
15+
}
16+
);
17+
})
18+
)
19+
collectModules
20+
;
21+
in
22+
lib.pipe
23+
(collectModules ./. home.options._module.args.value.moduleType.getSubModules (
24+
{
25+
inherit (home) options;
26+
lib = lib.extend (_: _: { hm = home.options.lib.value; });
27+
specialArgs = home.options._module.specialArgs.value;
28+
config = home.config // {
29+
inherit (home.options) _module;
30+
};
31+
}
32+
// home.options._module.specialArgs.value
33+
))
34+
[
35+
(lib.map (lib.getAttr "_file"))
36+
lib.unique
37+
]

utils/get-imports.nu

Lines changed: 24 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,24 @@
1+
#!/usr/bin/env nix-shell
2+
#!nix-shell -i nu -p nushell nix jq
3+
4+
def main [
5+
type: string,
6+
system: string
7+
] {
8+
let thisSource = nix flake archive --json | jq -r '.path'
9+
10+
let all_imports = if $type == "OS" {
11+
nix eval --impure --json $".#nixosConfigurations.($system)" --apply $"(cat ./utils/get-os-imports.nix)" | from json
12+
} else if $type == "HOME" {
13+
nix eval --impure --json $".#homeConfigurations.($system)" --apply $"(cat ./utils/get-hm-imports.nix)" | from json
14+
} else {
15+
echo "Invalid type. Use 'OS' or 'HOME'."
16+
return
17+
}
18+
19+
let our_imports = $all_imports
20+
| filter { $in | str starts-with $thisSource }
21+
| each { $in | str substring (($thisSource | str length) + 1).. }
22+
23+
echo $our_imports | to json
24+
}

utils/get-os-imports.nix

Lines changed: 35 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,35 @@
1+
system:
2+
let
3+
inherit (system) lib;
4+
5+
# Doing this to silence the warning about collectModules being an internal function
6+
inherit
7+
(
8+
(import (system._module.specialArgs.flake.inputs.nixpkgs + "/lib/modules.nix") {
9+
lib = lib.extend (
10+
_: prev: {
11+
trivial = prev.trivial // {
12+
warn = _: y: y;
13+
};
14+
}
15+
);
16+
})
17+
)
18+
collectModules
19+
;
20+
in
21+
lib.pipe
22+
(collectModules ./. system.type.getSubModules (
23+
{
24+
inherit (system) lib options;
25+
inherit (system._module) specialArgs;
26+
config = system.config // {
27+
inherit (system) _module;
28+
};
29+
}
30+
// system._module.specialArgs
31+
))
32+
[
33+
(lib.map (lib.getAttr "_file"))
34+
lib.unique
35+
]

0 commit comments

Comments
 (0)