lol
1{
2 lib,
3 stdenv,
4 stdenvNoCC,
5 fetchFromGitHub,
6 rustPlatform,
7 electron_35,
8 nodejs_22,
9 yarn-berry_4,
10 cacert,
11 writableTmpDirAsHomeHook,
12 cargo,
13 rustc,
14 findutils,
15 zip,
16 rsync,
17 jq,
18 copyDesktopItems,
19 makeWrapper,
20 llvmPackages,
21 apple-sdk_15,
22 makeDesktopItem,
23 nix-update-script,
24 buildType ? "stable",
25 commandLineArgs ? "",
26}:
27let
28 hostPlatform = stdenvNoCC.hostPlatform;
29 nodePlatform = hostPlatform.node.platform;
30 nodeArch = hostPlatform.node.arch;
31 electron = electron_35;
32 nodejs = nodejs_22;
33 yarn-berry = yarn-berry_4.override { inherit nodejs; };
34 productName = if buildType != "stable" then "AFFiNE-${buildType}" else "AFFiNE";
35 binName = lib.toLower productName;
36in
37stdenv.mkDerivation (finalAttrs: {
38 pname = binName;
39
40 version = "0.24.1";
41 src = fetchFromGitHub {
42 owner = "toeverything";
43 repo = "AFFiNE";
44 tag = "v${finalAttrs.version}";
45 hash = "sha256-Yq5TD5yInv+0d1S6M58I8CneCAGUwH0ThGrEJfLIrX0=";
46 };
47
48 cargoDeps = rustPlatform.fetchCargoVendor {
49 inherit (finalAttrs) pname version src;
50 hash = "sha256-tRDc7Rky59Rh08QTNiG3yopErHJzARxN8BZGrSUECLE=";
51 };
52 yarnOfflineCache = stdenvNoCC.mkDerivation {
53 name = "yarn-offline-cache";
54 inherit (finalAttrs) src;
55 nativeBuildInputs = [
56 yarn-berry
57 cacert
58 writableTmpDirAsHomeHook
59 ];
60 # force yarn install run in CI mode
61 env.CI = "1";
62 buildPhase =
63 let
64 supportedArchitectures = builtins.toJSON {
65 os = [
66 "darwin"
67 "linux"
68 ];
69 cpu = [
70 "x64"
71 "ia32"
72 "arm64"
73 ];
74 libc = [
75 "glibc"
76 "musl"
77 ];
78 };
79 in
80 ''
81 runHook preBuild
82
83 mkdir -p $out
84 yarn config set enableTelemetry false
85 yarn config set cacheFolder $out
86 yarn config set enableGlobalCache false
87 yarn config set supportedArchitectures --json '${supportedArchitectures}'
88
89 yarn install --immutable --mode=skip-build
90
91 runHook postBuild
92 '';
93 dontInstall = true;
94 outputHashMode = "recursive";
95 outputHash = "sha256-U2FGvdtGiM97aXmbfNIfi87hvwDkd1dvlAABYiDgAGI=";
96 };
97
98 buildInputs = lib.optionals hostPlatform.isDarwin [
99 apple-sdk_15
100 ];
101
102 nativeBuildInputs = [
103 nodejs
104 yarn-berry
105 cargo
106 rustc
107 findutils
108 zip
109 jq
110 rsync
111 writableTmpDirAsHomeHook
112 ]
113 ++ lib.optionals hostPlatform.isLinux [
114 copyDesktopItems
115 makeWrapper
116 ]
117 ++ lib.optionals hostPlatform.isDarwin [
118 # bindgenHook is needed to build `coreaudio-sys` on darwin
119 rustPlatform.bindgenHook
120 ];
121
122 env = {
123 # force yarn install run in CI mode
124 CI = "1";
125 # `LIBCLANG_PATH` is needed to build `coreaudio-sys` on darwin
126 LIBCLANG_PATH = lib.optionalString hostPlatform.isDarwin "${lib.getLib llvmPackages.libclang}/lib";
127 };
128
129 # Remove code under The AFFiNE Enterprise Edition (EE) license.
130 # Keep file package.json for `yarn install --immutable` lockfile check.
131 postPatch = ''
132 BACKEND_SERVER_PACKAGE_JSON="$(jq 'del(.scripts.postinstall)' packages/backend/server/package.json)"
133 rm -rf packages/backend/server/{.*,*}
134 echo "$BACKEND_SERVER_PACKAGE_JSON" > packages/backend/server/package.json
135 '';
136
137 configurePhase = ''
138 runHook preConfigure
139
140 # cargo config
141 mkdir -p .cargo
142 cat $cargoDeps/.cargo/config.toml >> .cargo/config.toml
143 ln -s $cargoDeps @vendor@
144
145 # yarn config
146 yarn config set enableTelemetry false
147 yarn config set enableGlobalCache false
148 yarn config set cacheFolder $yarnOfflineCache
149
150 # electron config
151 ELECTRON_VERSION_IN_LOCKFILE=$(yarn why electron --json | tail --lines 1 | jq --raw-output '.children | to_entries | first | .key ' | cut -d : -f 2)
152 rsync --archive --chmod=u+w "${electron.dist}/" $HOME/.electron-prebuilt-zip-tmp
153 export ELECTRON_FORGE_ELECTRON_ZIP_DIR=$PWD/.electron_zip_dir
154 mkdir -p $ELECTRON_FORGE_ELECTRON_ZIP_DIR
155 (cd $HOME/.electron-prebuilt-zip-tmp && zip --recurse-paths - .) > $ELECTRON_FORGE_ELECTRON_ZIP_DIR/electron-v$ELECTRON_VERSION_IN_LOCKFILE-${nodePlatform}-${nodeArch}.zip
156 export ELECTRON_SKIP_BINARY_DOWNLOAD=1
157
158 runHook postConfigure
159 '';
160
161 buildPhase = ''
162 runHook preBuild
163
164 # first build
165 yarn install
166 CARGO_NET_OFFLINE=true yarn affine @affine/native build
167 GITHUB_SHA=ffffffffffffffffffffffffffffffffffffffff BUILD_TYPE=${buildType} SKIP_NX_CACHE=1 yarn affine @affine/electron generate-assets
168
169 # second build
170 yarn config set nmMode classic
171 yarn config set nmHoistingLimits workspaces
172 find . -name 'node_modules' -type d -prune -exec rm -rf '{}' +
173 yarn install
174 BUILD_TYPE=${buildType} SKIP_WEB_BUILD=1 SKIP_BUNDLE=1 HOIST_NODE_MODULES=1 yarn affine @affine/electron make
175
176 runHook postBuild
177 '';
178
179 installPhase =
180 if hostPlatform.isDarwin then
181 ''
182 runHook preInstall
183
184 mkdir -p $out/Applications
185 mv packages/frontend/apps/electron/out/${buildType}/${productName}-darwin-${nodeArch}/${productName}.app $out/Applications
186
187 runHook postInstall
188 ''
189 else
190 ''
191 runHook preInstall
192
193 mkdir --parents $out/lib/${binName}/
194 mv packages/frontend/apps/electron/out/${buildType}/${productName}-linux-${nodeArch}/{resources,LICENSE*} $out/lib/${binName}/
195 install -Dm644 packages/frontend/apps/electron/resources/icons/icon_${buildType}_64x64.png $out/share/icons/hicolor/64x64/apps/${binName}.png
196
197 makeWrapper "${lib.getExe electron}" $out/bin/${binName} \
198 --inherit-argv0 \
199 --add-flags $out/lib/${binName}/resources/app.asar \
200 --add-flags "\''${NIXOS_OZONE_WL:+\''${WAYLAND_DISPLAY:+--ozone-platform-hint=auto --enable-features=WaylandWindowDecorations --enable-wayland-ime=true}}" \
201 --add-flags ${lib.escapeShellArg commandLineArgs}
202
203 runHook postInstall
204 '';
205
206 desktopItems = [
207 (makeDesktopItem {
208 name = binName;
209 desktopName = productName;
210 comment = "AFFiNE Desktop App";
211 exec = "${binName} %U";
212 terminal = false;
213 icon = binName;
214 startupWMClass = binName;
215 categories = [ "Utility" ];
216 mimeTypes = [ "x-scheme-handler/${binName}" ];
217 })
218 ];
219
220 passthru.updateScript = nix-update-script {
221 extraArgs = [
222 "--version-regex=^v(\\d+\\.\\d+\\.\\d+)$"
223 ];
224 };
225
226 meta = {
227 description = "Workspace with fully merged docs, whiteboards and databases";
228 longDescription = ''
229 AFFiNE is an open-source, all-in-one workspace and an operating
230 system for all the building blocks that assemble your knowledge
231 base and much more -- wiki, knowledge management, presentation
232 and digital assets
233 '';
234 homepage = "https://affine.pro/";
235 license = lib.licenses.mit;
236 maintainers = with lib.maintainers; [ xiaoxiangmoe ];
237 platforms = [
238 "aarch64-darwin"
239 "aarch64-linux"
240 "x86_64-darwin"
241 "x86_64-linux"
242 ];
243 sourceProvenance = [ lib.sourceTypes.fromSource ];
244 };
245})