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