1{
2 bash,
3 buildDotnetModule,
4 coreutils,
5 darwin,
6 dotnetCorePackages,
7 fetchFromGitHub,
8 fetchpatch,
9 gitMinimal,
10 glibc,
11 glibcLocales,
12 lib,
13 nixosTests,
14 stdenv,
15 which,
16 buildPackages,
17 runtimeShell,
18 # List of Node.js runtimes the package should support
19 nodeRuntimes ? [
20 "node20"
21 "node24"
22 ],
23 nodejs_20,
24 nodejs_24,
25}:
26
27# Node.js runtimes supported by upstream
28assert builtins.all (
29 x:
30 builtins.elem x [
31 "node20"
32 "node24"
33 ]
34) nodeRuntimes;
35
36buildDotnetModule (finalAttrs: {
37 pname = "github-runner";
38 version = "2.328.0";
39
40 src = fetchFromGitHub {
41 owner = "actions";
42 repo = "runner";
43 tag = "v${finalAttrs.version}";
44 hash = "sha256-3Q2bscLKdUBPx+5X0qxwtcy3CU6N/wE8yO1CcATSyBQ=";
45 leaveDotGit = true;
46 postFetch = ''
47 git -C $out rev-parse --short HEAD > $out/.git-revision
48 rm -rf $out/.git
49 '';
50 };
51
52 # The git commit is read during the build and some tests depend on a git repo to be present
53 # https://github.com/actions/runner/blob/22d1938ac420a4cb9e3255e47a91c2e43c38db29/src/dir.proj#L5
54 unpackPhase = ''
55 cp -r $src $TMPDIR/src
56 chmod -R +w $TMPDIR/src
57 cd $TMPDIR/src
58 (
59 export PATH=${buildPackages.git}/bin:$PATH
60 git init
61 git config user.email "root@localhost"
62 git config user.name "root"
63 git add .
64 git commit -m "Initial commit"
65 git checkout -b v${finalAttrs.version}
66 )
67 mkdir -p $TMPDIR/bin
68 cat > $TMPDIR/bin/git <<EOF
69 #!${runtimeShell}
70 if [ \$# -eq 1 ] && [ "\$1" = "rev-parse" ]; then
71 echo $(cat $TMPDIR/src/.git-revision)
72 exit 0
73 fi
74 exec ${buildPackages.git}/bin/git "\$@"
75 EOF
76 chmod +x $TMPDIR/bin/git
77 export PATH=$TMPDIR/bin:$PATH
78 '';
79
80 patches = [
81 # Replace some paths that originally point to Nix's read-only store
82 ./patches/host-context-dirs.patch
83 # Use GetDirectory() to obtain "diag" dir
84 ./patches/use-get-directory-for-diag.patch
85 # Don't try to install service
86 ./patches/dont-install-service.patch
87 # Access `.env` and `.path` relative to `$RUNNER_ROOT`, if set
88 ./patches/env-sh-use-runner-root.patch
89 # Fix FHS path: https://github.com/actions/runner/pull/2464
90 (fetchpatch {
91 name = "ln-fhs.patch";
92 url = "https://github.com/actions/runner/commit/5ff0ce1.patch";
93 hash = "sha256-2Vg3cKZK3cE/OcPDZkdN2Ro2WgvduYTTwvNGxwCfXas=";
94 })
95 # Fix source path discovery in tests
96 ./patches/test-getsrcpath.patch
97 ];
98
99 postPatch = ''
100 # Ignore changes to src/Runner.Sdk/BuildConstants.cs
101 substituteInPlace src/dir.proj \
102 --replace-fail 'git update-index --assume-unchanged ./Runner.Sdk/BuildConstants.cs' \
103 'true'
104 '';
105
106 DOTNET_SYSTEM_GLOBALIZATION_INVARIANT = isNull glibcLocales;
107 LOCALE_ARCHIVE = lib.optionalString (
108 !finalAttrs.DOTNET_SYSTEM_GLOBALIZATION_INVARIANT
109 ) "${glibcLocales}/lib/locale/locale-archive";
110
111 postConfigure = ''
112 # Generate src/Runner.Sdk/BuildConstants.cs
113 dotnet msbuild \
114 -t:GenerateConstant \
115 -p:ContinuousIntegrationBuild=true \
116 -p:Deterministic=true \
117 -p:PackageRuntime="${dotnetCorePackages.systemToDotnetRid stdenv.hostPlatform.system}" \
118 -p:RunnerVersion="${finalAttrs.version}" \
119 src/dir.proj
120 '';
121
122 nativeBuildInputs = [
123 which
124 gitMinimal
125 # needed for `uname`
126 coreutils
127 ]
128 ++ lib.optionals (stdenv.hostPlatform.isDarwin && stdenv.hostPlatform.isAarch64) [
129 darwin.autoSignDarwinBinariesHook
130 ];
131
132 buildInputs = [
133 (lib.getLib stdenv.cc.cc)
134 bash
135 ];
136
137 dotnet-sdk = dotnetCorePackages.sdk_8_0;
138 dotnet-runtime = dotnetCorePackages.runtime_8_0;
139
140 dotnetFlags = [
141 "-p:PackageRuntime=${dotnetCorePackages.systemToDotnetRid stdenv.hostPlatform.system}"
142 ];
143
144 # As given here: https://github.com/actions/runner/blob/0befa62/src/dir.proj#L33-L41
145 projectFile = [
146 "src/Sdk/Sdk.csproj"
147 "src/Runner.Common/Runner.Common.csproj"
148 "src/Runner.Listener/Runner.Listener.csproj"
149 "src/Runner.Worker/Runner.Worker.csproj"
150 "src/Runner.PluginHost/Runner.PluginHost.csproj"
151 "src/Runner.Sdk/Runner.Sdk.csproj"
152 "src/Runner.Plugins/Runner.Plugins.csproj"
153 ];
154 nugetDeps = ./deps.json;
155
156 doCheck = true;
157
158 __darwinAllowLocalNetworking = true;
159
160 # Fully qualified name of disabled tests
161 disabledTests = [
162 "GitHub.Runner.Common.Tests.Listener.SelfUpdaterL0.TestSelfUpdateAsync"
163 "GitHub.Runner.Common.Tests.ProcessInvokerL0.OomScoreAdjIsInherited"
164 # intermittently failing
165 "GitHub.Runner.Common.Tests.ProcessExtensionL0.SuccessReadProcessEnv"
166 ]
167 ++ map (x: "GitHub.Runner.Common.Tests.Listener.SelfUpdaterL0.TestSelfUpdateAsync_${x}") [
168 "Cancel_CloneHashTask_WhenNotNeeded"
169 "CloneHash_RuntimeAndExternals"
170 "DownloadRetry"
171 "FallbackToFullPackage"
172 "NoUpdateOnOldVersion"
173 "NotUseExternalsRuntimeTrimmedPackageOnHashMismatch"
174 "UseExternalsRuntimeTrimmedPackage"
175 "UseExternalsTrimmedPackage"
176 "ValidateHash"
177 ]
178 ++ map (x: "GitHub.Runner.Common.Tests.Listener.SelfUpdaterV2L0.${x}") [
179 "TestSelfUpdateAsync_DownloadRetry"
180 "TestSelfUpdateAsync_ValidateHash"
181 "TestSelfUpdateAsync"
182 ]
183 ++ map (x: "GitHub.Runner.Common.Tests.Worker.ActionManagerL0.PrepareActions_${x}") [
184 "CompositeActionWithActionfile_CompositeContainerNested"
185 "CompositeActionWithActionfile_CompositePrestepNested"
186 "CompositeActionWithActionfile_MaxLimit"
187 "CompositeActionWithActionfile_Node"
188 "DownloadActionFromGraph"
189 "NotPullOrBuildImagesMultipleTimes"
190 "RepositoryActionWithActionYamlFile_DockerHubImage"
191 "RepositoryActionWithActionfileAndDockerfile"
192 "RepositoryActionWithActionfile_DockerHubImage"
193 "RepositoryActionWithActionfile_Dockerfile"
194 "RepositoryActionWithActionfile_DockerfileRelativePath"
195 "RepositoryActionWithActionfile_Node"
196 "RepositoryActionWithDockerfile"
197 "RepositoryActionWithDockerfileInRelativePath"
198 "RepositoryActionWithDockerfilePrepareActions_Repository"
199 "RepositoryActionWithInvalidWrapperActionfile_Node"
200 "RepositoryActionWithWrapperActionfile_PreSteps"
201 ]
202 ++ map (x: "GitHub.Runner.Common.Tests.DotnetsdkDownloadScriptL0.${x}") [
203 "EnsureDotnetsdkBashDownloadScriptUpToDate"
204 "EnsureDotnetsdkPowershellDownloadScriptUpToDate"
205 ]
206 ++ [ "GitHub.Runner.Common.Tests.Listener.RunnerL0.TestRunOnceHandleUpdateMessage" ]
207 # Tests for trimmed runner packages which aim at reducing the update size. Not relevant for Nix.
208 ++ map (x: "GitHub.Runner.Common.Tests.PackagesTrimL0.${x}") [
209 "RunnerLayoutParts_CheckExternalsHash"
210 "RunnerLayoutParts_CheckDotnetRuntimeHash"
211 ]
212 # Strictly require a Debug configuration to work
213 ++ [
214 # https://github.com/actions/runner/blob/da3412e/src/Runner.Common/HostContext.cs#L260-L266
215 "GitHub.Runner.Common.Tests.HostContextL0.AuthMigrationAutoReset"
216 ]
217 ++ lib.optionals (stdenv.hostPlatform.system == "aarch64-linux") [
218 # "JavaScript Actions in Alpine containers are only supported on x64 Linux runners. Detected Linux Arm64"
219 "GitHub.Runner.Common.Tests.Worker.StepHostL0.DetermineNodeRuntimeVersionInAlpineContainerAsync"
220 "GitHub.Runner.Common.Tests.Worker.StepHostL0.DetermineNode20RuntimeVersionInAlpineContainerAsync"
221 "GitHub.Runner.Common.Tests.Worker.StepHostL0.DetermineNode24RuntimeVersionInAlpineContainerAsync"
222 ]
223 ++ lib.optionals finalAttrs.DOTNET_SYSTEM_GLOBALIZATION_INVARIANT [
224 "GitHub.Runner.Common.Tests.Util.StringUtilL0.FormatUsesInvariantCulture"
225 "GitHub.Runner.Common.Tests.Worker.VariablesL0.Constructor_SetsOrdinalIgnoreCaseComparer"
226 "GitHub.Runner.Common.Tests.Worker.WorkerL0.DispatchCancellation"
227 "GitHub.Runner.Common.Tests.Worker.WorkerL0.DispatchRunNewJob"
228 ];
229
230 testProjectFile = [ "src/Test/Test.csproj" ];
231
232 preCheck = ''
233 # Required by some tests
234 export GITHUB_ACTIONS_RUNNER_TRACE=1
235 mkdir -p _layout/externals
236 ''
237 + lib.optionalString (lib.elem "node20" nodeRuntimes) ''
238 ln -s ${nodejs_20} _layout/externals/node20
239 ''
240 + lib.optionalString (lib.elem "node24" nodeRuntimes) ''
241 ln -s ${nodejs_24} _layout/externals/node24
242 '';
243
244 postInstall = ''
245 mkdir -p $out/bin
246
247 install -m755 src/Misc/layoutbin/runsvc.sh $out/lib/github-runner
248 install -m755 src/Misc/layoutbin/RunnerService.js $out/lib/github-runner
249 install -m755 src/Misc/layoutroot/run.sh $out/lib/github-runner
250 install -m755 src/Misc/layoutroot/run-helper.sh.template $out/lib/github-runner/run-helper.sh
251 install -m755 src/Misc/layoutroot/config.sh $out/lib/github-runner
252 install -m755 src/Misc/layoutroot/env.sh $out/lib/github-runner
253
254 # env.sh is patched to not require any wrapping
255 ln -sr "$out/lib/github-runner/env.sh" "$out/bin/"
256
257 substituteInPlace $out/lib/github-runner/config.sh \
258 --replace './bin/Runner.Listener' "$out/bin/Runner.Listener"
259 ''
260 + lib.optionalString stdenv.hostPlatform.isLinux ''
261 substituteInPlace $out/lib/github-runner/config.sh \
262 --replace 'command -v ldd' 'command -v ${glibc.bin}/bin/ldd' \
263 --replace 'ldd ./bin' '${glibc.bin}/bin/ldd ${finalAttrs.dotnet-runtime}/share/dotnet/shared/Microsoft.NETCore.App/${finalAttrs.dotnet-runtime.version}/' \
264 --replace '/sbin/ldconfig' '${glibc.bin}/bin/ldconfig'
265 ''
266 + ''
267 # Remove uneeded copy for run-helper template
268 substituteInPlace $out/lib/github-runner/run.sh --replace 'cp -f "$DIR"/run-helper.sh.template "$DIR"/run-helper.sh' ' '
269 substituteInPlace $out/lib/github-runner/run-helper.sh --replace '"$DIR"/bin/' '"$DIR"/'
270
271 # Make paths absolute
272 substituteInPlace $out/lib/github-runner/runsvc.sh \
273 --replace './externals' "$out/lib/externals" \
274 --replace './bin/RunnerService.js' "$out/lib/github-runner/RunnerService.js"
275
276 # The upstream package includes Node and expects it at the path
277 # externals/node$version. As opposed to the official releases, we don't
278 # link the Alpine Node flavors.
279 mkdir -p $out/lib/externals
280 ''
281 + lib.optionalString (lib.elem "node20" nodeRuntimes) ''
282 ln -s ${nodejs_20} $out/lib/externals/node20
283 ''
284 + lib.optionalString (lib.elem "node24" nodeRuntimes) ''
285 ln -s ${nodejs_24} $out/lib/externals/node24
286 ''
287 + ''
288 # Install Nodejs scripts called from workflows
289 install -D src/Misc/layoutbin/hashFiles/index.js $out/lib/github-runner/hashFiles/index.js
290 mkdir -p $out/lib/github-runner/checkScripts
291 install src/Misc/layoutbin/checkScripts/* $out/lib/github-runner/checkScripts/
292 ''
293 + lib.optionalString stdenv.hostPlatform.isLinux ''
294 # Wrap explicitly to, e.g., prevent extra entries for LD_LIBRARY_PATH
295 makeWrapperArgs=()
296
297 # We don't wrap with libicu
298 substituteInPlace $out/lib/github-runner/config.sh \
299 --replace '$LDCONFIG_COMMAND -NXv ''${libpath//:/ }' 'echo libicu'
300 ''
301 + ''
302 # XXX: Using the corresponding Nix argument does not work as expected:
303 # https://github.com/NixOS/nixpkgs/issues/218449
304 # Common wrapper args for `executables`
305 makeWrapperArgs+=(
306 --run 'export RUNNER_ROOT="''${RUNNER_ROOT:-"$HOME/.github-runner"}"'
307 --run 'mkdir -p "$RUNNER_ROOT"'
308 --chdir "$out"
309 )
310 '';
311
312 # List of files to wrap
313 executables = [
314 "config.sh"
315 "Runner.Listener"
316 "Runner.PluginHost"
317 "Runner.Worker"
318 "run.sh"
319 "runsvc.sh"
320 ];
321
322 doInstallCheck = true;
323 installCheckPhase = ''
324 runHook preInstallCheck
325
326 export RUNNER_ROOT="$TMPDIR"
327
328 $out/bin/config.sh --help >/dev/null
329 $out/bin/Runner.Listener --help >/dev/null
330
331 version=$($out/bin/Runner.Listener --version)
332 if [[ "$version" != "${finalAttrs.version}" ]]; then
333 printf 'Unexpected version %s' "$version"
334 exit 1
335 fi
336
337 commit=$($out/bin/Runner.Listener --commit)
338 if [[ "$commit" != "$(git rev-parse HEAD)" ]]; then
339 printf 'Unexpected commit %s' "$commit"
340 exit 1
341 fi
342
343 runHook postInstallCheck
344 '';
345
346 passthru = {
347 tests.smoke-test = nixosTests.github-runner;
348 updateScript = ./update.sh;
349 };
350
351 meta = {
352 changelog = "https://github.com/actions/runner/releases/tag/v${finalAttrs.version}";
353 description = "Self-hosted runner for GitHub Actions";
354 homepage = "https://github.com/actions/runner";
355 license = lib.licenses.mit;
356 maintainers = with lib.maintainers; [
357 veehaitch
358 kfollesdal
359 aanderse
360 zimbatm
361 ];
362 platforms = [
363 "x86_64-linux"
364 "aarch64-linux"
365 "x86_64-darwin"
366 "aarch64-darwin"
367 ];
368 sourceProvenance = with lib.sourceTypes; [ binaryNativeCode ];
369 };
370})