diff --git a/README.md b/README.md index 9cde15d..c4cf546 100644 --- a/README.md +++ b/README.md @@ -34,7 +34,8 @@ Currently, following expressions are exported: | ----------------------- | ------------------------------------------------------------------------ | | `pkgs-sources.stable` | Stable `nixpkgs` source (based on `release-22.05` `nixpkgs` branch) | | `pkgs-sources.unstable` | Unstable `nixpkgs` source (based on `nixpkgs-unstable` `nixpkgs` branch) | -| `tools.haskell` | Various functions to make building Haskell development tools | +| `tools.devshell` | Various functions for building a development shell | +| `tools.haskell` | Various functions for building Haskell development tools | ## License diff --git a/default.nix b/default.nix index d360428..20b05e4 100644 --- a/default.nix +++ b/default.nix @@ -14,6 +14,7 @@ in ## Categorized tools. tools = { + devshell = import ./tools/devshell { pkgs = import sources.nixpkgs { }; }; haskell = import ./tools/haskell.nix; }; } diff --git a/nix/sources.json b/nix/sources.json index b5e8573..aa2d84b 100644 --- a/nix/sources.json +++ b/nix/sources.json @@ -1,14 +1,14 @@ { "nixpkgs": { - "branch": "release-22.05", + "branch": "release-22.11", "description": "Nix Packages collection", "homepage": "", "owner": "NixOS", "repo": "nixpkgs", - "rev": "82379884b2e9cf1ba65f5b14bbcb9d1438abb745", - "sha256": "0zh7h0hsbasl4d527zz7mr2msnffpf269si3jpw2sd62lk8zikz4", + "rev": "7300dca340ad08729891f843284bd385a1fac7fb", + "sha256": "1w25z6r71klrhv3bbqlg2pjlxpl6smkg84i32ij51840qhx5iq6c", "type": "tarball", - "url": "https://github.com/NixOS/nixpkgs/archive/82379884b2e9cf1ba65f5b14bbcb9d1438abb745.tar.gz", + "url": "https://github.com/NixOS/nixpkgs/archive/7300dca340ad08729891f843284bd385a1fac7fb.tar.gz", "url_template": "https://github.com///archive/.tar.gz" }, "nixpkgs-unstable": { @@ -17,10 +17,10 @@ "homepage": "", "owner": "NixOS", "repo": "nixpkgs", - "rev": "ff9793cfd1a25145a7e591af604675b3d6f68987", - "sha256": "087fgyq4q1nr44h52zz3q3h21i1svgip27fp5csa5mz6yzqkqakv", + "rev": "0c9aadc8eff6daaa5149d2df9e6c49baaf44161c", + "sha256": "0ya3v073pyb9bhw4ky362sk423ficygsr8ng7spmnmji1zmlpj40", "type": "tarball", - "url": "https://github.com/NixOS/nixpkgs/archive/ff9793cfd1a25145a7e591af604675b3d6f68987.tar.gz", + "url": "https://github.com/NixOS/nixpkgs/archive/0c9aadc8eff6daaa5149d2df9e6c49baaf44161c.tar.gz", "url_template": "https://github.com///archive/.tar.gz" } } diff --git a/tools/devshell/default.nix b/tools/devshell/default.nix new file mode 100644 index 0000000..0390d86 --- /dev/null +++ b/tools/devshell/default.nix @@ -0,0 +1,6 @@ +{ pkgs, ... }: + +let + mkDevshell = import ./devsh.nix { pkgs = pkgs; }; +in +mkDevshell diff --git a/tools/devshell/devsh.nix b/tools/devshell/devsh.nix new file mode 100644 index 0000000..bb9bc20 --- /dev/null +++ b/tools/devshell/devsh.nix @@ -0,0 +1,118 @@ +{ pkgs }: + +let + ## Create devsh script: + scriptDevsh = pkgs.writeScriptBin "devsh" (builtins.readFile ./devsh.py); + + ## Example usage: + ## + ## ``` + ## { compiler ? "ghc92" }: + ## + ## let + ## ## Import this codebase's Nix helper set: + ## nix = import ./nix { compiler = compiler; }; + ## + ## ## Get packages: + ## pkgs = nix.pkgs; + ## + ## ## Create the devshell: + ## devshell = nix.telosnix.tools.devshell { + ## name = "devshell-example"; + ## src = ./.; + ## quickstart = ./README_quickstart.md; + ## docs = [ + ## { name = "readme"; title = "Introduction"; path = "./README.md"; } + ## { name = "quickstart"; title = "Quickstart"; path = "./README_quickstart.md"; } + ## { name = "another-guide"; title = "Another Guide"; path = "./README_guide.ronn"; } + ## ]; + ## extensions = { + ## my-command = { + ## help = "Help for my command"; + ## exec = "${my-command}/bin/my-command"; + ## }; + ## }; + ## }; + ## in + ## pkgs.mkShell { + ## buildInputs = [ + ## devshell + ## ] ++ nix.haskell-dev-tools; + ## + ## shellHook = '' + ## devsh welcome + ## echo + ## devsh exec + ## + ## ## Make sure that doctest finds correct GHC executable and libraries: + ## export NIX_GHC=${nix.ghc}/bin/ghc + ## export NIX_GHC_LIBDIR=${nix.ghc}/lib/${nix.ghc.meta.name} + ## ''; + ## } + ## ``` + mkDevshell = { name, src, quickstart, docs ? [ ], extensions ? { } }: + with pkgs; + let + extensionDrvs = lib.attrsets.mapAttrs (name: value: writeShellScriptBin "devsh-extension-${name}" "${value.exec} \"\${@}\"") extensions; + extensionHelp = lib.strings.concatStrings (lib.attrsets.attrValues (lib.attrsets.mapAttrs (name: value: "${name}: ${value.help}\n") extensions)); + extensionHelpFile = writeTextFile { name = "extensions.dat"; text = extensionHelp; }; + binPathDevsh = lib.makeBinPath ([ figlet lolcat rich-cli ] ++ (lib.attrsets.attrValues extensionDrvs)); + in + stdenv.mkDerivation { + name = name; + src = src; + + nativeBuildInputs = [ makeWrapper ]; + + installPhase = '' + ## Create output directories: + mkdir -p $out/bin + mkdir -p $out/etc + mkdir -p $out/share/doc/guide/src + mkdir -p $out/share/doc/guide/html + + ## Copy quickstart guide: + cp "${quickstart}" $out/share/doc/quickstart.md + + ## Copy devshell extensions help: + cp "${extensionHelpFile}" $out/share/doc/extensions.dat + + ## Copy the devshell guide sections: + ${toString (map ({name, title, path}: "cp \"${path}\" $out/share/doc/guide/src/${name}.md\n") docs)} + + ## Write devshell guide index: + cat < $out/share/doc/guide/src/SUMMARY.md + ${toString (map ({name, title, path}: "- [${title}](${name}.md)\n") docs)} + EOF + + ## Write devshell guide configuration: + cat < $out/share/doc/guide/book.toml + [book] + title = "${name}" + description = "This is the Development Shell Guide for ${name}." + + [output.html] + default-theme = "light" + preferred-dark-theme = "ayu" + curly-quotes = true + mathjax-support = true + copy-fonts = true + EOF + + ## Build devshell guide: + "${mdbook}/bin/mdbook" build --dest-dir $out/share/doc/guide/html $out/share/doc/guide + + ## Copy scripts to the output destination: + cp ${scriptDevsh}/bin/devsh $out/bin/ + + ## Wrap devsh program: + wrapProgram $out/bin/devsh \ + --prefix PATH : ${binPathDevsh} \ + --set DEVSHELL_NAME "${name}" \ + --set DEVSHELL_DOCS_DIR "$out/share/doc" \ + --set DEVSHELL_QUICKSTART "$out/share/doc/quickstart.md" \ + --set DEVSHELL_EXTENSIONS "$out/share/doc/extensions.dat" + ''; + }; +in +mkDevshell diff --git a/tools/devshell/devsh.py b/tools/devshell/devsh.py new file mode 100644 index 0000000..d4328a7 --- /dev/null +++ b/tools/devshell/devsh.py @@ -0,0 +1,94 @@ +#!/usr/bin/env python3 + +# pyright: reportConstantRedefinition=false + +import argparse +import os +import subprocess as sp +import sys +from typing import Any + +DEVSHELL_NAME = os.environ["DEVSHELL_NAME"] +DEVSHELL_DOCS_DIR = os.environ["DEVSHELL_DOCS_DIR"] +DEVSHELL_QUICKSTART = os.environ["DEVSHELL_QUICKSTART"] +DEVSHELL_EXTENSIONS = os.environ["DEVSHELL_EXTENSIONS"] +DEVSHELL_EXTENSIONS_HELP = "" + +with open(DEVSHELL_EXTENSIONS) as cfile: + provided = cfile.read().strip() + if provided: + DEVSHELL_EXTENSIONS_HELP = f"""List of Extension Commands: +=========================== + +{provided} + +These extension commands can be executed as: + +devsh exec +""" + + +def main() -> None: + parser = argparse.ArgumentParser( + description="devshell", + epilog=DEVSHELL_EXTENSIONS_HELP, + formatter_class=argparse.RawTextHelpFormatter, + ) + + subparsers = parser.add_subparsers() + + subparser = subparsers.add_parser("banner", help="Print banner") + subparser.set_defaults(func=do_banner) + + subparser = subparsers.add_parser("quickstart", help="Print quickstart guide") + subparser.set_defaults(func=do_quickstart) + + subparser = subparsers.add_parser("welcome", help="Print welcome notice") + subparser.set_defaults(func=do_welcome) + + subparser = subparsers.add_parser("guide", help="Open developer's guide") + subparser.set_defaults(func=do_guide) + + subparser = subparsers.add_parser("exec", help="Execute an extension command") + subparser.add_argument( + "args", + nargs=argparse.REMAINDER, + help="Arguments to pass to the extension command", + ) + subparser.set_defaults(func=do_exec) + + args = parser.parse_args(args=None if sys.argv[1:] else ["--help"]) + args.func(args) + + +def do_banner(_args: Any) -> None: + ps = sp.Popen(("figlet", "-c", "-t", DEVSHELL_NAME), stdout=sp.PIPE) + sp.run(("lolcat", "-S", "20", "-p", "1", "-F", "0.02"), stdin=ps.stdout, check=True) + ps.wait() + + +def do_quickstart(_args: Any) -> None: + sp.run(("rich", "-c", "-w", "80", DEVSHELL_QUICKSTART), check=True) + + +def do_welcome(args: Any) -> None: + do_banner(args) + do_quickstart(args) + + +def do_guide(_args: Any) -> None: + sp.run(("xdg-open", f"{DEVSHELL_DOCS_DIR}/guide/html/index.html"), check=True) + + +def do_exec(args: Any) -> None: + if not args.args: + sys.stdout.write(DEVSHELL_EXTENSIONS_HELP) + return + + args.args[0] = f"devsh-extension-{args.args[0]}" + + sp.run(args.args, check=True, stdin=sys.stdin) + + +if __name__ == "__main__": + main() diff --git a/tools/haskell.nix b/tools/haskell.nix index e7193e1..40cad61 100644 --- a/tools/haskell.nix +++ b/tools/haskell.nix @@ -18,9 +18,7 @@ let ## }; overrideHaskellForDevTools = new: old: { - Cabal = old.Cabal_3_6_3_0; - fourmolu = old.fourmolu_0_8_2_0; - ghc-lib-parser = old.ghc-lib-parser_9_2_4_20220729; + ## Placeholder for overriding Haskell packages in case required. }; ## Returns the Haskell set for development purposes.