1{ system ? builtins.currentSystem
3, iosSdkVersion ? "10.2"
4, __useLegacyCompilers ? false
7 cleanSource = builtins.filterSource (name: _: let baseName = builtins.baseNameOf name; in !(
8 builtins.match "^\\.ghc\\.environment.*" baseName != null ||
9 baseName == "cabal.project.local"
12 commandRuntimeDeps = pkgs: with pkgs; [
20 beam = hackGet ./dep/beam;
21 constraints-extras = hackGet ./dep/constraints-extras;
22 dependent-map = hackGet ./dep/dependent-map;
23 dependent-monoidal-map = hackGet ./dep/dependent-monoidal-map;
24 dependent-sum-aeson-orphans = hackGet ./dep/dependent-sum-aeson-orphans;
25 gargoyle = hackGet ./dep/gargoyle;
26 postgresql-simple = hackGet ./dep/postgresql-simple;
27 reflex-aeson-orphans = hackGet ./dep/reflex-aeson-orphans;
28 vessel = hackGet ./dep/vessel;
31 getReflexPlatform = getReflexPlatform' __useLegacyCompilers;
32 getReflexPlatform' = __useLegacyCompilers: sys: import ./dep/reflex-platform {
33 inherit iosSdkVersion __useLegacyCompilers;
35 enableLibraryProfiling = profiling;
39 obeliskExecutableConfig = import ./lib/executable-config { nixpkgs = pkgs; filterGitSource = cleanSource; };
43 haskellOverlays = with pkgs.haskell.lib; [
45 # Fix misc upstream packages
47 pkgs = self.callPackage ({ pkgs }: pkgs) {};
49 hnix = pkgs.haskell.lib.dontCheck (self.callCabal2nix "hnix" (pkgs.fetchFromGitHub {
50 owner = "haskell-nix";
52 rev = "42afdc21da5d9e076eab57eaa42bfdde938192b8";
53 sha256 = "0psw384dx9bw2dp93xrzw8rd9amvcwgzn64jzzwby7sfspj6k349";
55 # Need 8.0.2 build support
56 # PR: https://github.com/dmwit/universe/pull/33
57 universe-template = self.callCabal2nix "universe-template" (pkgs.fetchFromGitHub {
58 owner = "obsidiansystems";
60 rev = "6a71119bfa5db2b9990a2491c941469ff8ef5d13";
61 sha256 = "0z8smyainnlzcglv3dlx6x1n9j6d2jv48aa8f2421iayfkxg3js5";
64 algebraic-graphs = pkgs.haskell.lib.doJailbreak
65 (self.callCabal2nix "algebraic-graphs" (pkgs.fetchFromGitHub {
66 owner = "snowleopard";
68 rev = "480a73137e9b38ad3f1bc2c628847953d2fb3e25";
69 sha256 = "0dpwi5ffs88brl3lz51bwb004c6zm8ds8pkw1vzsg2a6aaiyhlzl";
72 # Need ShowTag, EqTag, and OrdTag instances
73 dependent-sum-template = self.callCabal2nix "dependent-sum-template" (pkgs.fetchFromGitHub {
75 repo = "dependent-sum-template";
76 rev = "dcb92a98d35d79f712b9a398c8c356acc979ecff";
77 sha256 = "1v337d810d88jzfriw07pr16d34nibm6q2zkw787lc3sfn07glp5";
80 beam-core = dontCheck (self.callCabal2nix "beam-core" (src.beam + /beam-core) {});
81 beam-postgres = dontCheck (self.callCabal2nix "beam-postgres" (src.beam + /beam-postgres) {});
82 beam-migrate = self.callCabal2nix "beam-migrate" (src.beam + /beam-migrate) {};
84 postgresql-simple = dontCheck (self.callCabal2nix "postgresql-simple" src.postgresql-simple {});
85 constraints-extras = self.callCabal2nix "constraints-extras" src.constraints-extras {};
86 dependent-map = self.callCabal2nix "dependent-map" src.dependent-map {};
87 dependent-monoidal-map = self.callCabal2nix "dependent-monoidal-map" src.dependent-monoidal-map {};
88 dependent-sum-aeson-orphans = self.callCabal2nix "dependent-sum-aeson-orphans" src.dependent-sum-aeson-orphans {};
89 reflex-aeson-orphans = self.callCabal2nix "reflex-aeson-orphans" src.reflex-aeson-orphans {};
91 websockets = self.callCabal2nix "websockets" (pkgs.fetchFromGitHub {
92 owner = "obsidiansystems";
94 rev = "b750edf10fc6f532f9fb36588904491af87606c7";
95 sha256 = "1wd0gkx8a4x6n5cdkra96zvpv8l49i0z90amvrng70ybnzbxpfi5";
98 vessel = self.callCabal2nix "vessel" src.vessel {};
100 gargoyle = doJailbreak (self.callCabal2nix "gargoyle" (src.gargoyle + /gargoyle) {});
101 gargoyle-postgresql-nix = pkgs.haskell.lib.addBuildTool
102 (self.callCabal2nix "gargoyle-postgresql-nix" (src.gargoyle + /gargoyle-postgresql-nix) {})
103 pkgs.postgresql100; # `staticWhich` requires `psql` on PATH during build time
104 gargoyle-postgresql = addBuildDepend (doJailbreak (self.callCabal2nix "gargoyle-postgresql" (src.gargoyle + /gargoyle-postgresql) {})) pkgs.postgresql100;
105 postgresql-libpq = enableCabalFlag (overrideCabal super.postgresql-libpq (drv: {
106 pkgconfigDepends = (drv.pkgconfigDepends or []) ++ [pkgs.postgresql100]; #TODO: Obelisk should probably provide this version of postgresql
107 })) "use-pkg-config";
110 # Add obelisk packages
112 pkgs = self.callPackage ({ pkgs }: pkgs) {};
114 obelisk-executable-config = pkgs.obeliskExecutableConfig.haskellPackage self;
115 obelisk-executable-config-inject = pkgs.obeliskExecutableConfig.platforms.web.inject self;
117 obelisk-asset-manifest = self.callCabal2nix "obelisk-asset-manifest" (hackGet ./lib/asset + "/manifest") {};
118 obelisk-asset-serve-snap = self.callCabal2nix "obelisk-asset-serve-snap" (hackGet ./lib/asset + "/serve-snap") {};
119 obelisk-backend = self.callCabal2nix "obelisk-backend" (cleanSource ./lib/backend) {};
120 obelisk-cliapp = self.callCabal2nix "obelisk-cliapp" (cleanSource ./lib/cliapp) {};
121 obelisk-command = self.callCabal2nix "obelisk-command" (cleanSource ./lib/command) {};
122 obelisk-frontend = self.callCabal2nix "obelisk-frontend" (cleanSource ./lib/frontend) {};
123 obelisk-request = self.callCabal2nix "obelisk-request" (cleanSource ./lib/request) {};
124 obelisk-run = self.callCabal2nix "obelisk-run" (cleanSource ./lib/run) {};
125 obelisk-route = self.callCabal2nix "obelisk-route" (cleanSource ./lib/route) {};
126 obelisk-selftest = self.callCabal2nix "obelisk-selftest" (cleanSource ./lib/selftest) {};
127 obelisk-snap-extras = self.callCabal2nix "obelisk-snap-extras" (cleanSource ./lib/snap-extras) {};
131 pkgs = self.callPackage ({ pkgs }: pkgs) {};
132 haskellLib = pkgs.haskell.lib;
134 # Modify a Haskell package to add completion scripts for the given
135 # executable produced by it. These completion scripts will be picked up
136 # automatically if the resulting derivation is installed, e.g. by
138 addOptparseApplicativeCompletionScripts = exeName: pkg: haskellLib.overrideCabal pkg (drv: {
139 postInstall = (drv.postInstall or "") + ''
140 BASH_COMP_DIR="$out/share/bash-completion/completions"
141 mkdir -p "$BASH_COMP_DIR"
142 "$out/bin/${exeName}" --bash-completion-script "$out/bin/${exeName}" >"$BASH_COMP_DIR/ob"
144 ZSH_COMP_DIR="$out/share/zsh/vendor-completions"
145 mkdir -p "$ZSH_COMP_DIR"
146 "$out/bin/${exeName}" --zsh-completion-script "$out/bin/${exeName}" >"$ZSH_COMP_DIR/_ob"
148 FISH_COMP_DIR="$out/share/fish/vendor_completions.d"
149 mkdir -p "$FISH_COMP_DIR"
150 "$out/bin/${exeName}" --fish-completion-script "$out/bin/${exeName}" >"$FISH_COMP_DIR/ob.fish"
154 # Dynamic linking with split objects dramatically increases startup time (about
155 # 0.5 seconds on a decent machine with SSD), so we do `justStaticExecutables`.
156 obelisk-command = haskellLib.overrideCabal
157 (addOptparseApplicativeCompletionScripts "ob"
158 (haskellLib.justStaticExecutables super.obelisk-command))
160 buildTools = (drv.buildTools or []) ++ [ pkgs.buildPackages.makeWrapper ];
162 ${drv.postFixup or ""}
163 # Make `ob` reference its runtime dependencies.
164 wrapProgram "$out"/bin/ob --prefix PATH : ${pkgs.lib.makeBinPath (commandRuntimeDeps pkgs)}
167 obelisk-selftest = haskellLib.justStaticExecutables super.obelisk-selftest;
173 reflex-platform = getReflexPlatform' false system;
174 inherit (reflex-platform) hackGet nixpkgs;
177 # The haskell environment used to build Obelisk itself, e.g. the 'ob' command
178 ghcObelisk = reflex-platform.ghc;
180 # Development environments for obelisk packages.
181 ghcObeliskEnvs = pkgs.lib.mapAttrs (n: v: reflex-platform.workOn ghcObelisk v) ghcObelisk;
183 inherit (import ./lib/asset/assets.nix { inherit nixpkgs; }) mkAssets;
185 haskellLib = pkgs.haskell.lib;
188 inherit reflex-platform;
189 inherit (reflex-platform) nixpkgs pinBuildInputs;
190 inherit (nixpkgs) lib;
191 pathGit = ./.; # Used in CI by the migration graph hash algorithm to correctly ignore files.
192 path = reflex-platform.filterGit ./.;
193 obelisk = ghcObelisk;
194 obeliskEnvs = ghcObeliskEnvs;
195 command = ghcObelisk.obelisk-command;
196 shell = pinBuildInputs "obelisk-shell" ([command] ++ commandRuntimeDeps pkgs) [];
198 selftest = pkgs.writeScript "selftest" ''
202 PATH="${command}/bin:$PATH"
203 export OBELISK_IMPL="${hackGet ./.}"
204 "${ghcObelisk.obelisk-selftest}/bin/obelisk-selftest" +RTS -N -RTS "$@"
206 #TODO: Why can't I build ./skeleton directly as a derivation? `nix-build -E ./.` doesn't work
207 skeleton = pkgs.runCommand "skeleton" {
208 dir = builtins.filterSource (path: type: builtins.trace path (baseNameOf path != ".obelisk")) ./skeleton;
212 nullIfAbsent = p: if lib.pathExists p then p else null;
213 #TODO: Avoid copying files within the nix store. Right now, obelisk-asset-manifest-generate copies files into a big blob so that the android/ios static assets can be imported from there; instead, we should get everything lined up right before turning it into an APK, so that copies, if necessary, only exist temporarily.
214 processAssets = { src, packageName ? "obelisk-generated-static", moduleName ? "Obelisk.Generated.Static" }: pkgs.runCommand "asset-manifest" {
216 outputs = [ "out" "haskellManifest" "symlinked" ];
217 nativeBuildInputs = [ ghcObelisk.obelisk-asset-manifest ];
221 mkdir -p "$symlinked"
222 obelisk-asset-manifest-generate "$src" "$haskellManifest" ${packageName} ${moduleName} "$symlinked"
225 compressedJs = frontend: optimizationLevel: pkgs.runCommand "compressedJs" { buildInputs = [ pkgs.closurecompiler ]; } ''
228 ln -s "${haskellLib.justStaticExecutables frontend}/bin/frontend.jsexe/all.js" all.unminified.js
229 closure-compiler --externs "${reflex-platform.ghcjsExternsJs}" -O ${optimizationLevel} --jscomp_warning=checkVars --create_source_map="all.js.map" --source_map_format=V3 --js_output_file="all.js" all.unminified.js
230 echo "//# sourceMappingURL=all.js.map" >> all.js
234 mkBaseEc2 = { hostName, routeHost, enableHttps, adminEmail, ... }: {...}: {
236 (pkgs.path + /nixos/modules/virtualisation/amazon-image.nix)
240 firewall.allowedTCPPorts = if enableHttps then [ 80 443 ] else [ 80 ];
242 security.acme.certs = if enableHttps then {
243 "${routeHost}".email = adminEmail;
256 , internalPort ? 8000
257 , backendArgs ? "--port=${toString internalPort}"
262 virtualHosts."${routeHost}" = {
263 enableACME = enableHttps;
264 forceSSL = enableHttps;
265 locations.${baseUrl} = {
266 proxyPass = "http://localhost:" + toString internalPort;
270 systemd.services.${name} = {
271 wantedBy = [ "multi-user.target" ];
272 after = [ "network.target" ];
273 restartIfChanged = true;
277 exec ./backend ${backendArgs} >>backend.out 2>>backend.err </dev/null
281 KillMode = "process";
282 WorkingDirectory = "~";
289 description = "${user} service";
290 home = "/var/lib/${user}";
295 groups.${group} = {};
300 serverExe = backend: frontend: assets: config: optimizationLevel: version:
301 pkgs.runCommand "serverExe" {} ''
304 ln -s "${haskellLib.justStaticExecutables backend}"/bin/* $out/
305 ln -s "${mkAssets assets}" $out/static.assets
306 cp -r ${config} $out/config
307 ln -s ${mkAssets (compressedJs frontend optimizationLevel)} $out/frontend.jsexe.assets
308 echo ${version} > $out/version
311 server = { exe, hostName, adminEmail, routeHost, enableHttps, config, version }@args:
313 nixos = import (pkgs.path + /nixos);
315 system = "x86_64-linux";
318 (serverModules.mkBaseEc2 args)
319 (serverModules.mkObeliskApp args)
325 # An Obelisk project is a reflex-platform project with a predefined layout and role for each component
326 project = base: projectDefinition:
327 let configPath = base + "/config";
328 projectOut = sys: (getReflexPlatform sys).project (args@{ nixpkgs, ... }:
329 let mkProject = { android ? null #TODO: Better error when missing
330 , ios ? null #TODO: Better error when missing
332 , overrides ? _: _: {}
333 , staticFiles ? base + /static
335 , shellToolOverrides ? _: _: {}
336 , withHoogle ? false # Setting this to `true` makes shell reloading far slower
337 , __closureCompilerOptimizationLevel ? "ADVANCED"
339 let frontendName = "frontend";
340 backendName = "backend";
341 commonName = "common";
342 staticName = "obelisk-generated-static";
343 staticFilesImpure = if lib.isDerivation staticFiles then staticFiles else toString staticFiles;
344 processedStatic = processAssets { src = staticFiles; };
345 # The packages whose names and roles are defined by this package
346 predefinedPackages = lib.filterAttrs (_: x: x != null) {
347 ${frontendName} = nullIfAbsent (base + "/frontend");
348 ${commonName} = nullIfAbsent (base + "/common");
349 ${backendName} = nullIfAbsent (base + "/backend");
351 combinedPackages = predefinedPackages // packages;
352 projectOverrides = self: super: {
353 ${staticName} = haskellLib.dontHaddock (self.callCabal2nix staticName processedStatic.haskellManifest {});
354 ${backendName} = haskellLib.addBuildDepend super.${backendName} self.obelisk-run;
356 totalOverrides = lib.composeExtensions projectOverrides overrides;
357 inherit (lib.strings) hasPrefix;
358 privateConfigDirs = ["config/backend"];
359 injectableConfig = builtins.filterSource (path: _:
360 !(lib.lists.any (x: hasPrefix (toString base + "/" + toString x) (toString path)) privateConfigDirs)
363 inherit shellToolOverrides tools withHoogle;
364 overrides = totalOverrides;
365 packages = combinedPackages;
367 ${if android == null && ios == null then null else "ghcSavedSplices"} = (lib.filter (x: lib.hasAttr x combinedPackages) [
371 ghc = (lib.filter (x: lib.hasAttr x combinedPackages) [
376 ghcjs = lib.filter (x: lib.hasAttr x combinedPackages) [
382 ${if android == null then null else frontendName} = {
383 executableName = "frontend";
384 ${if builtins.pathExists staticFiles then "assets" else null} =
385 nixpkgs.obeliskExecutableConfig.platforms.android.inject injectableConfig processedStatic.symlinked;
389 ${if ios == null then null else frontendName} = {
390 executableName = "frontend";
391 ${if builtins.pathExists staticFiles then "staticSrc" else null} =
392 nixpkgs.obeliskExecutableConfig.platforms.ios.inject injectableConfig processedStatic.symlinked;
395 passthru = { inherit android ios packages overrides tools shellToolOverrides withHoogle staticFiles staticFilesImpure __closureCompilerOptimizationLevel; };
397 in mkProject (projectDefinition args));
398 serverOn = sys: config: version: serverExe
399 (projectOut sys).ghc.backend
400 (projectOut system).ghcjs.frontend
401 (projectOut sys).passthru.staticFiles
403 (projectOut sys).passthru.__closureCompilerOptimizationLevel
405 linuxExe = serverOn "x86_64-linux";
406 dummyVersion = "Version number is only available for deployments";
407 in projectOut system // {
408 linuxExeConfigurable = linuxExe;
409 linuxExe = linuxExe (base + "/config") dummyVersion;
410 exe = serverOn system (base + "/config") dummyVersion;
411 server = args@{ hostName, adminEmail, routeHost, enableHttps, config, version }: let
412 injectableConfig = builtins.filterSource (path: _: !(lib.hasPrefix (toString config + "/backend") (toString path))) config;
413 in server (args // { exe = linuxExe injectableConfig version; });
414 obelisk = import (base + "/.obelisk/impl") {};
416 haskellPackageSets = {
417 inherit (reflex-platform) ghc ghcjs;
keysesc to close
- j k
- move down, up
- enter
- open
- esc
- back, or up a level
- 1 2 3 4
- readme, files, commits, refs
- /
- filter
- y
- copy the clone command
- c
- compare the chosen commit with another (or shift-click both)
- ?
- show or hide this list