1{ system ? builtins.currentSystem
3, iosSdkVersion ? "10.2"
6 getReflexPlatform = sys: import ./dep/reflex-platform { inherit iosSdkVersion; system = sys; };
7 reflex-platform = getReflexPlatform system;
8 inherit (reflex-platform) hackGet nixpkgs;
10in with pkgs.haskell.lib; with pkgs.lib;
12 # TODO: Remove this after updating nixpkgs: https://github.com/NixOS/nixpkgs/issues/37750
13 justStaticExecutables' = drv: let
14 drv' = justStaticExecutables drv;
15 in if pkgs.stdenv.isDarwin
16 then removeConfigureFlag drv' "--ghc-option=-optl=-dead_strip"
19 commandRuntimeDeps = pkgs: with pkgs; [
29 # Modify a Haskell package to add completion scripts for the given
30 # executable produced by it. These completion scripts will be picked up
31 # automatically if the resulting derivation is installed, e.g. by
33 addOptparseApplicativeCompletionScripts = exeName: pkg: overrideCabal pkg (drv: {
34 postInstall = (drv.postInstall or "") + ''
35 BASH_COMP_DIR="$out/share/bash-completion/completions"
36 mkdir -p "$BASH_COMP_DIR"
37 "$out/bin/${exeName}" --bash-completion-script "$out/bin/${exeName}" >"$BASH_COMP_DIR/ob"
39 ZSH_COMP_DIR="$out/share/zsh/vendor-completions"
40 mkdir -p "$ZSH_COMP_DIR"
41 "$out/bin/${exeName}" --zsh-completion-script "$out/bin/${exeName}" >"$ZSH_COMP_DIR/_ob"
43 FISH_COMP_DIR="$out/share/fish/vendor_completions.d"
44 mkdir -p "$FISH_COMP_DIR"
45 "$out/bin/${exeName}" --fish-completion-script "$out/bin/${exeName}" >"$FISH_COMP_DIR/ob.fish"
49 # The haskell environment used to build Obelisk itself, e.g. the 'ob' command
50 ghcObelisk = reflex-platform.ghc.override {
51 overrides = composeExtensions defaultHaskellOverrides (self: super: {
52 mkDerivation = args: super.mkDerivation (args // {
53 enableLibraryProfiling = profiling;
56 # Dynamic linking with split objects dramatically increases startup time (about 0.5 seconds on a decent machine with SSD)
57 obelisk-command = addOptparseApplicativeCompletionScripts "ob" (justStaticExecutables' super.obelisk-command);
59 optparse-applicative = self.callHackage "optparse-applicative" "0.14.0.0" {};
61 Cabal = self.callHackage "Cabal" "2.0.0.2" {};
65 fixUpstreamPkgs = self: super: {
66 megaparsec = self.callHackage "megaparsec" "6.0.0" {};
67 modern-uri = pkgs.haskell.lib.dontCheck super.modern-uri;
68 github = pkgs.haskell.lib.doJailbreak (self.callCabal2nix "github" (pkgs.fetchFromGitHub {
72 sha256 = "0gzqhpvgab9j0v5647m65d3jjy8kdlwaqyr5pc2hjyc0w8gvgcak";
75 algebraic-graphs = pkgs.haskell.lib.dontCheck (pkgs.haskell.lib.doJailbreak
76 (self.callCabal2nix "algebraic-graphs" (pkgs.fetchFromGitHub {
77 owner = "snowleopard";
79 rev = "480a73137e9b38ad3f1bc2c628847953d2fb3e25";
80 sha256 = "0dpwi5ffs88brl3lz51bwb004c6zm8ds8pkw1vzsg2a6aaiyhlzl";
84 let src = pkgs.fetchFromGitHub {
85 owner = "obsidiansystems";
86 repo = "monoidal-containers";
87 rev = "af5f6cedd1acd8725b19fd6a0277f83906603491";
88 sha256 = "11v20ing8lrb5ccf6g9iihwcw5d22yj2ifw15v04ypn19y8kariw";
90 in pkgs.haskell.lib.dontCheck (self.callCabal2nix "monoidal-containers" src {});
92 # Need deriveSomeUniverse
93 # PR: https://github.com/dmwit/universe/pull/32
94 universe-template = self.callCabal2nix "universe-template" (pkgs.fetchFromGitHub {
95 owner = "obsidiansystems";
97 rev = "5a2fc823caa4163411d7e41aa80e67cefb15944a";
98 sha256 = "0ll2z0fh18z6x8jl8kbp7ldagwccz3wjmvrw1gw752z058n82yfa";
101 # Need ShowTag, EqTag, and OrdTag instances
102 dependent-sum-template = self.callCabal2nix "dependent-sum-template" (pkgs.fetchFromGitHub {
104 repo = "dependent-sum-template";
105 rev = "bfe9c37f4eaffd8b17c03f216c06a0bfb66f7df7";
106 sha256 = "1w3s7nvw0iw5li3ry7s8r4651qwgd22hmgz6by0iw3rm64fy8x0y";
110 cleanSource = builtins.filterSource (name: _: let baseName = builtins.baseNameOf name; in !(
111 builtins.match "^\\.ghc\\.environment.*" baseName != null ||
112 baseName == "cabal.project.local"
115 executableConfig = import ./lib/executable-config { nixpkgs = pkgs; filterGitSource = cleanSource; };
117 addLibs = self: super: {
118 obelisk-asset-manifest = self.callCabal2nix "obelisk-asset-manifest" (hackGet ./lib/asset + "/manifest") {};
119 obelisk-asset-serve-snap = self.callCabal2nix "obelisk-asset-serve-snap" (hackGet ./lib/asset + "/serve-snap") {};
120 obelisk-backend = self.callCabal2nix "obelisk-backend" (cleanSource ./lib/backend) {};
121 obelisk-cliapp = self.callCabal2nix "obelisk-cliapp" (cleanSource ./lib/cliapp) {};
122 obelisk-command = (self.callCabal2nix "obelisk-command" (cleanSource ./lib/command) {});
123 obelisk-executable-config = executableConfig.haskellPackage self;
124 obelisk-executable-config-inject = executableConfig.platforms.web.inject self; # TODO handle platforms.{ios,android}
125 obelisk-frontend = self.callCabal2nix "obelisk-frontend" (cleanSource ./lib/frontend) {};
126 obelisk-migration = self.callCabal2nix "obelisk-migration" (cleanSource ./lib/migration) {};
127 obelisk-run = self.callCabal2nix "obelisk-run" (cleanSource ./lib/run) {};
128 obelisk-route = self.callCabal2nix "obelisk-route" (cleanSource ./lib/route) {};
129 obelisk-selftest = self.callCabal2nix "obelisk-selftest" (cleanSource ./lib/selftest) {};
130 obelisk-snap = self.callCabal2nix "obelisk-snap" (cleanSource ./lib/snap) {};
131 obelisk-snap-extras = self.callCabal2nix "obelisk-snap-extras" (cleanSource ./lib/snap-extras) {};
134 inherit (import ./lib/asset/assets.nix { inherit nixpkgs; }) mkAssets;
136 defaultHaskellOverrides = composeExtensions fixUpstreamPkgs addLibs;
140 inherit reflex-platform;
141 inherit (reflex-platform) nixpkgs pinBuildInputs;
142 pathGit = ./.; # Used in CI by the migration graph hash algorithm to correctly ignore files.
143 path = reflex-platform.filterGit ./.;
144 obelisk = ghcObelisk;
145 commandWithMigration = ghcObelisk.obelisk-command.overrideAttrs (drv: {
146 postInstall = (drv.postInstall or "") +
147 ''cp -r ${./migration} $out/migration;'';
149 command = pkgs.runCommand commandWithMigration.name { nativeBuildInputs = [pkgs.makeWrapper]; } ''
151 ln -s '${commandWithMigration}/bin/ob' "$out/bin/ob"
152 wrapProgram "$out"/bin/ob --prefix PATH : ${pkgs.lib.makeBinPath (commandRuntimeDeps pkgs)}
154 shell = pinBuildInputs "obelisk-shell" ([command] ++ commandRuntimeDeps pkgs) [];
156 selftest = pkgs.writeScript "selftest" ''
160 PATH="${command}/bin:$PATH"
161 export OBELISK_IMPL="${hackGet ./.}"
162 "${justStaticExecutables' ghcObelisk.obelisk-selftest}/bin/obelisk-selftest" "$@"
164 #TODO: Why can't I build ./skeleton directly as a derivation? `nix-build -E ./.` doesn't work
165 skeleton = pkgs.runCommand "skeleton" {
166 dir = builtins.filterSource (path: type: builtins.trace path (baseNameOf path != ".obelisk")) ./skeleton;
170 nullIfAbsent = p: if pathExists p then p else null;
171 haskellOverrides = addLibs;
172 #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.
173 processAssets = { src, packageName ? "static", moduleName ? "Static" }: pkgs.runCommand "asset-manifest" {
175 outputs = [ "out" "haskellManifest" "symlinked" ];
177 (reflex-platform.ghc.callCabal2nix "obelisk-asset-manifest" (hackGet ./lib/asset + "/manifest") {})
182 obelisk-asset-manifest-generate "$src" "$haskellManifest" ${packageName} ${moduleName} "$symlinked"
185 compressedJs = frontend: pkgs.runCommand "compressedJs" { buildInputs = [ pkgs.closurecompiler ]; } ''
188 ln -s "${justStaticExecutables frontend}/bin/frontend.jsexe/all.js" all.unminified.js
189 closure-compiler --externs "${reflex-platform.ghcjsExternsJs}" -O ADVANCED --jscomp_warning=checkVars --create_source_map="all.js.map" --source_map_format=V3 --js_output_file="all.js" all.unminified.js
190 echo "//# sourceMappingURL=all.js.map" >> all.js
193 serverExe = backend: frontend: assets: config:
194 pkgs.runCommand "serverExe" {} ''
197 ln -s "${justStaticExecutables backend}"/bin/backend $out/backend
198 ln -s "${mkAssets assets}" $out/static.assets
199 cp -r ${config} $out/config
200 ln -s ${mkAssets (compressedJs frontend)} $out/frontend.jsexe.assets
203 server = { exe, hostName, adminEmail, routeHost, enableHttps, ... }:
204 let system = "x86_64-linux";
205 nixos = import (pkgs.path + /nixos);
209 configuration = args: {
211 (pkgs.path + /nixos/modules/virtualisation/amazon-image.nix)
215 firewall.allowedTCPPorts = if enableHttps then [ 80 443 ] else [ 80 ];
219 virtualHosts."${routeHost}" = {
220 enableACME = enableHttps;
221 forceSSL = enableHttps;
223 proxyPass = "http://127.0.0.1:" + toString backendPort;
227 security.acme.certs = if enableHttps then {
228 "${routeHost}".email = adminEmail;
230 systemd.services.backend = {
231 wantedBy = [ "multi-user.target" ];
232 after = [ "network.target" ];
233 restartIfChanged = true;
237 exec ./backend >>backend.out 2>>backend.err </dev/null
241 KillMode = "process";
242 WorkingDirectory = "~";
247 users.extraUsers.backend = {
248 description = "backend service";
249 home = "/var/lib/backend";
259 # An Obelisk project is a reflex-platform project with a predefined layout and role for each component
260 project = base: projectDefinition:
261 let configPath = base + "/config";
262 static = base + "/static";
263 processedStatic = processAssets { src = static; };
264 projectOut = sys: (getReflexPlatform sys).project (args@{ nixpkgs, ... }:
265 let mkProject = { android ? null #TODO: Better error when missing
266 , ios ? null #TODO: Better error when missing
268 , overrides ? _: _: {}
270 let frontendName = "frontend";
271 backendName = "backend";
272 commonName = "common";
273 staticName = "static";
274 staticPath = base + "/static";
275 # The packages whose names and roles are defined by this package
276 predefinedPackages = filterAttrs (_: x: x != null) {
277 ${frontendName} = nullIfAbsent (base + "/frontend");
278 ${commonName} = nullIfAbsent (base + "/common");
279 ${backendName} = nullIfAbsent (base + "/backend");
281 combinedPackages = predefinedPackages // packages;
282 projectOverrides = self: super: {
283 ${staticName} = dontHaddock (self.callCabal2nix "static" processedStatic.haskellManifest {});
284 ${backendName} = addBuildDepend super.${backendName} self.obelisk-run;
286 totalOverrides = composeExtensions (composeExtensions defaultHaskellOverrides projectOverrides) overrides;
288 overrides = totalOverrides;
289 packages = combinedPackages;
291 ghc = (filter (x: hasAttr x combinedPackages) [
296 ghcjs = filter (x: hasAttr x combinedPackages) [
301 withHoogle = false; # Setting this to `true` makes shell reloading far slower
303 ${if android == null then null else frontendName} = {
304 executableName = "frontend";
305 ${if builtins.pathExists staticPath then "assets" else null} = processedStatic.symlinked;
309 ${if ios == null then null else frontendName} = {
310 executableName = "frontend";
311 ${if builtins.pathExists staticPath then "staticSrc" else null} = processedStatic.symlinked;
315 in mkProject (projectDefinition args));
316 serverOn = sys: config: serverExe (projectOut sys).ghc.backend (projectOut system).ghcjs.frontend static config;
317 # `exe` is project's backend executable, with frontend assets, config, etc.
318 # `linuxExe` is the same but built for x86_64-linux.
319 exe = serverOn system configPath;
320 linuxExe = serverOn "x86_64-linux" configPath;
321 in projectOut system // {
322 inherit exe linuxExe;
323 server = args@{ hostName, adminEmail, routeHost, enableHttps, config }:
324 server (args // { exe = serverOn "x86_64-linux" config;});
325 obelisk = import (base + "/.obelisk/impl") {};
327 haskellPackageSets = {
328 ghc = reflex-platform.ghc.override {
329 overrides = defaultHaskellOverrides;
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