my nixos/home-manager configuration
1{
2 pname,
3 version,
4 src,
5 meta,
6 binaryName,
7 desktopName,
8 self,
9 buildFHSEnv,
10 makeDesktopItem,
11 lib,
12 stdenv,
13 alsa-lib,
14 at-spi2-atk,
15 at-spi2-core,
16 atk,
17 cairo,
18 cups,
19 dbus,
20 expat,
21 fontconfig,
22 freetype,
23 gdk-pixbuf,
24 glib,
25 gtk3,
26 libcxx,
27 glibc,
28 libdrm,
29 libglvnd,
30 libnotify,
31 libpulseaudio,
32 libuuid,
33 libva,
34 libX11,
35 libXScrnSaver,
36 libXcomposite,
37 libXcursor,
38 libXdamage,
39 libXext,
40 libXfixes,
41 libXi,
42 libXrandr,
43 libXrender,
44 libXtst,
45 libxcb,
46 libxshmfence,
47 libgbm,
48 nspr,
49 nss,
50 pango,
51 systemdLibs,
52 libappindicator-gtk3,
53 libdbusmenu,
54 writeScript,
55 pipewire,
56 libxkbcommon,
57 mesa,
58 python3,
59 runCommand,
60 libunity,
61 speechd-minimal,
62 wayland,
63 branch,
64 withOpenASAR ? false,
65 openasar,
66 withVencord ? false,
67 vencord,
68 withEquicord ? false,
69 equicord,
70 withMoonlight ? false,
71 moonlight,
72 withTTS ? true,
73 enableAutoscroll ? false,
74 # Disabling this would normally break Discord.
75 # The intended use-case for this is when SKIP_HOST_UPDATE is enabled via other means,
76 # for example if a settings.json is linked declaratively (e.g., with home-manager).
77 disableUpdates ? true,
78 # Make disabling the Chromium sandbox opt-in; recent Electron no longer needs this.
79 disableChromiumSandbox ? false,
80 commandLineArgs ? "",
81}:
82
83let
84 discordMods = [
85 withVencord
86 withEquicord
87 withMoonlight
88 ];
89 enabledDiscordModsCount = builtins.length (lib.filter (x: x) discordMods);
90
91 disableBreakingUpdates =
92 runCommand "disable-breaking-updates.py"
93 {
94 pythonInterpreter = "${python3.interpreter}";
95 configDirName = lib.toLower binaryName;
96 meta.mainProgram = "disable-breaking-updates.py";
97 }
98 ''
99 mkdir -p $out/bin
100 cp ${./disable-breaking-updates.py} $out/bin/disable-breaking-updates.py
101 substituteAllInPlace $out/bin/disable-breaking-updates.py
102 chmod +x $out/bin/disable-breaking-updates.py
103 '';
104
105 discordDir = stdenv.mkDerivation {
106 name = "${pname}-${version}-dir";
107 inherit src;
108
109 dontPatchELF = true;
110 dontStrip = true;
111 dontPatchShebangs = true;
112
113 dontBuild = true;
114 dontConfigure = true;
115
116 installPhase = ''
117 runHook preInstall
118
119 mkdir -p $out/opt/${binaryName}
120 cp -a ./. $out/opt/${binaryName}
121 cd $out/opt/${binaryName}
122
123 ${lib.optionalString withOpenASAR ''
124 cp -f ${openasar} resources/app.asar
125 ''}
126 ${lib.optionalString withVencord ''
127 mv resources/app.asar resources/_app.asar
128 mkdir resources/app.asar
129 echo '{"name":"discord","main":"index.js"}' > resources/app.asar/package.json
130 echo 'require("${vencord}/patcher.js")' > resources/app.asar/index.js
131 ''}
132 ${lib.optionalString withEquicord ''
133 mv resources/app.asar resources/_app.asar
134 mkdir resources/app.asar
135 echo '{"name":"discord","main":"index.js"}' > resources/app.asar/package.json
136 echo 'require("${equicord}/desktop/patcher.js")' > resources/app.asar/index.js
137 ''}
138 ${lib.optionalString withMoonlight ''
139 mv resources/app.asar resources/_app.asar
140 mkdir resources/app
141 echo '{"name":"discord","main":"injector.js","private": true}' > resources/app/package.json
142 echo 'require("${moonlight}/injector.js").inject(require("path").join(__dirname, "../_app.asar"));' > resources/app/injector.js
143 ''}
144
145 runHook postInstall
146 '';
147 };
148
149 fhsEnv = buildFHSEnv {
150 name = "${pname}-fhs";
151
152 targetPkgs =
153 pkgs:
154 [
155 libcxx
156 glibc
157 systemdLibs
158 libpulseaudio
159 libdrm
160 libgbm
161 stdenv.cc.cc.lib
162 alsa-lib
163 atk
164 at-spi2-atk
165 at-spi2-core
166 cairo
167 cups
168 dbus
169 expat
170 fontconfig
171 freetype
172 gdk-pixbuf
173 glib
174 gtk3
175 libglvnd
176 libnotify
177 libX11
178 libXcomposite
179 libXcursor
180 libXdamage
181 libXext
182 libXfixes
183 libXi
184 libXrandr
185 libXrender
186 libXtst
187 libXScrnSaver
188 libxcb
189 libxshmfence
190 libuuid
191 libva
192 nspr
193 nss
194 pango
195 pipewire
196 libxkbcommon
197 mesa
198 libappindicator-gtk3
199 libdbusmenu
200 libunity
201 wayland
202 ]
203 ++ lib.optionals withTTS [ speechd-minimal ];
204
205 multiPkgs = pkgs: [
206 alsa-lib
207 libpulseaudio
208 ];
209
210 runScript = writeScript "${pname}-wrapper" ''
211 #!${stdenv.shell}
212 set -euo pipefail
213
214 ${lib.optionalString disableUpdates ''
215 ${lib.getExe disableBreakingUpdates}
216 ''}
217
218 if [[ -n "''${XDG_DATA_DIRS:-}" ]]; then
219 export XDG_DATA_DIRS="${gtk3}/share/gsettings-schemas/${gtk3.name}/:''${XDG_DATA_DIRS}"
220 else
221 export XDG_DATA_DIRS="${gtk3}/share/gsettings-schemas/${gtk3.name}/"
222 fi
223
224 extraArgs=()
225 if [[ -n "''${NIXOS_OZONE_WL:-}" && -n "''${WAYLAND_DISPLAY:-}" ]]; then
226 extraArgs+=(--ozone-platform=wayland --enable-features=WaylandWindowDecorations --enable-wayland-ime=true)
227 fi
228
229 ${lib.optionalString enableAutoscroll ''
230 extraArgs+=(--enable-blink-features=MiddleClickAutoscroll)
231 ''}
232
233 ${lib.optionalString disableChromiumSandbox ''
234 extraArgs+=(--no-sandbox --disable-gpu-sandbox)
235 ''}
236
237 ${lib.optionalString withTTS ''
238 if [[ "''${NIXOS_SPEECH:-default}" != "False" ]]; then
239 export NIXOS_SPEECH=True
240 extraArgs+=(--enable-speech-dispatcher)
241 else
242 unset NIXOS_SPEECH
243 fi
244 ''}
245
246 cmd=(
247 ${discordDir}/opt/${binaryName}/${binaryName}
248 )
249 cmd+=("''${extraArgs[@]}")
250 ${lib.optionalString (commandLineArgs != "") ''
251 cmd+=(${lib.escapeShellArg commandLineArgs})
252 ''}
253
254 exec "''${cmd[@]}" "$@"
255 '';
256 };
257
258 desktopItem = makeDesktopItem {
259 name = pname;
260 exec = binaryName;
261 icon = pname;
262 inherit desktopName;
263 genericName = meta.description;
264 categories = [
265 "Network"
266 "InstantMessaging"
267 ];
268 mimeTypes = [ "x-scheme-handler/discord" ];
269 startupWMClass = "discord";
270 };
271in
272assert lib.assertMsg (
273 enabledDiscordModsCount <= 1
274) "discord: Only one of Vencord, Equicord or Moonlight can be enabled at the same time";
275stdenv.mkDerivation {
276 inherit
277 pname
278 version
279 meta
280 ;
281
282 dontUnpack = true;
283 dontBuild = true;
284 dontConfigure = true;
285
286 installPhase = ''
287 runHook preInstall
288
289 mkdir -p $out/bin
290 mkdir -p $out/share/applications
291 mkdir -p $out/share/pixmaps
292 mkdir -p $out/share/icons/hicolor/256x256/apps
293
294 ln -s ${fhsEnv}/bin/${pname}-fhs $out/bin/${binaryName}
295 # Without || true the install would fail on case-insensitive filesystems
296 ln -s ${fhsEnv}/bin/${pname}-fhs $out/bin/${lib.strings.toLower binaryName} || true
297
298 ln -s ${discordDir}/opt/${binaryName}/discord.png $out/share/pixmaps/${pname}.png
299 ln -s ${discordDir}/opt/${binaryName}/discord.png $out/share/icons/hicolor/256x256/apps/${pname}.png
300
301 ln -s ${desktopItem}/share/applications/${pname}.desktop $out/share/applications/
302
303 runHook postInstall
304 '';
305
306 passthru = {
307 # make it possible to run disableBreakingUpdates standalone
308 inherit disableBreakingUpdates;
309 updateScript = ./update.py;
310
311 tests = {
312 withVencord = self.override {
313 withVencord = true;
314 };
315 withEquicord = self.override {
316 withEquicord = true;
317 };
318 withMoonlight = self.override {
319 withMoonlight = true;
320 };
321 withOpenASAR = self.override {
322 withOpenASAR = true;
323 };
324 };
325 };
326}