Preparing a Nix flake for a Python program (Migra, using Poetry)

I want to be able to use a program called Migra in my own projects and nix shell, like this:

nix shell github:djrobstep/migra

I’m a novice at both Nix and Python and I’m hoping to get some help from the community. Please be patient with me :smiley:

Here is what I tried so far.

I cloned the repository here: Files · nix · Tad Lispy / migra · GitLab and added a following flake.nix file:

{
  description = "Like diff but for Postgres schemas";

  inputs = {
    flake-compat = {
      url = "github:edolstra/flake-compat";
      flake = false;
    };
    flake-utils = {
      url = "github:numtide/flake-utils";
    };
    poetry2nix-src = {
      url = "github:nix-community/poetry2nix";
    };
  };

  outputs = { self, nixpkgs , flake-compat , flake-utils , poetry2nix-src }:
    flake-utils.lib.eachDefaultSystem (system:
      let
        pkgs = import nixpkgs {
          inherit system;
          overlays = [ poetry2nix-src.overlay ];
        };
      in rec {
        packages.migra = pkgs.poetry2nix.mkPoetryApplication {
          projectDir  = ./.;
        };
        defaultPackage = packages.migra;
      }
    );
}

Running nix build I got an error about missing build-system section in pyproject.toml, so I’ve added it:

[build-system]
requires = ["poetry-core>=1.0.0"]
build-backend = "poetry.core.masonry.api"

Now I’m getting a TypeError: can't intern String from pathlib. Complete log is here:

$ nix build --print-build-logs
warning: Git tree '/home/tad/Projects/migra' is dirty
python3.8-migra> Sourcing python-remove-tests-dir-hook
python3.8-migra> Sourcing python-catch-conflicts-hook.sh
python3.8-migra> Sourcing python-remove-bin-bytecode-hook.sh
python3.8-migra> Sourcing pip-build-hook
python3.8-migra> Using pipBuildPhase
python3.8-migra> Using pipShellHook
python3.8-migra> Sourcing pip-install-hook
python3.8-migra> Using pipInstallPhase
python3.8-migra> Sourcing python-imports-check-hook.sh
python3.8-migra> Using pythonImportsCheckPhase
python3.8-migra> Sourcing python-namespaces-hook
python3.8-migra> unpacking sources
python3.8-migra> unpacking source archive /nix/store/cd5dj9fxy9lijwmajh6pbcpq343nf25k-source
python3.8-migra> source root is source
python3.8-migra> setting SOURCE_DATE_EPOCH to timestamp 315619200 of file source/tests/test_migra.py
python3.8-migra> patching sources
python3.8-migra> configuring
python3.8-migra> no configure script, doing nothing
python3.8-migra> building
python3.8-migra> Executing pipBuildPhase
python3.8-migra> Creating a wheel...
python3.8-migra> WARNING: The directory '/homeless-shelter/.cache/pip' or its parent directory is not owned or is not writable by the current user. The cache has been disabled. Check the permissions and owner of that directory. If executing pip with sudo, you may want sudo's -H flag.
python3.8-migra> Processing /build/source
python3.8-migra>     Preparing wheel metadata ... error
python3.8-migra>     ERROR: Command errored out with exit status 1:
python3.8-migra>      command: /nix/store/yl69v76azrz4daiqksrhb8nnmdiqdjg9-python3-3.8.8/bin/python3.8 /nix/store/74jp1w5s9n45bx7zq6avk5w7n5xq8wlq-python3.8-pip-20.2.4/lib/python3.8/site-packages/pip/_vendor/pep517/_in_process.py prepare_metadata_for_build_wheel /build/tmpme3inu0g
python3.8-migra>          cwd: /build/pip-req-build-t4h7xccs
python3.8-migra>     Complete output (20 lines):
python3.8-migra>     Traceback (most recent call last):
python3.8-migra>       File "/nix/store/74jp1w5s9n45bx7zq6avk5w7n5xq8wlq-python3.8-pip-20.2.4/lib/python3.8/site-packages/pip/_vendor/pep517/_in_process.py", line 280, in <module>
python3.8-migra>         main()
python3.8-migra>       File "/nix/store/74jp1w5s9n45bx7zq6avk5w7n5xq8wlq-python3.8-pip-20.2.4/lib/python3.8/site-packages/pip/_vendor/pep517/_in_process.py", line 263, in main
python3.8-migra>         json_out['return_val'] = hook(**hook_input['kwargs'])
python3.8-migra>       File "/nix/store/74jp1w5s9n45bx7zq6avk5w7n5xq8wlq-python3.8-pip-20.2.4/lib/python3.8/site-packages/pip/_vendor/pep517/_in_process.py", line 133, in prepare_metadata_for_build_wheel
python3.8-migra>         return hook(metadata_directory, config_settings)
python3.8-migra>       File "/nix/store/88jrbsgwpv4yq6g2jijbp4mf9mmsn51g-python3.8-poetry-core-1.0.2/lib/python3.8/site-packages/poetry/core/masonry/api.py", line 43, in prepare_metadata_for_build_wheel
python3.8-migra>         poetry = Factory().create_poetry(Path(".").resolve(), with_dev=False)
python3.8-migra>       File "/nix/store/88jrbsgwpv4yq6g2jijbp4mf9mmsn51g-python3.8-poetry-core-1.0.2/lib/python3.8/site-packages/poetry/core/factory.py", line 71, in create_poetry
python3.8-migra>         package.readme = Path(poetry_file.parent) / local_config["readme"]
python3.8-migra>       File "/nix/store/sx1bdyn5hv9wlq2x2jw72qx1vmbzcgdm-python3.8-pathlib-1.0.1/lib/python3.8/site-packages/pathlib.py", line 853, in __truediv__
python3.8-migra>         return self._make_child((key,))
python3.8-migra>       File "/nix/store/sx1bdyn5hv9wlq2x2jw72qx1vmbzcgdm-python3.8-pathlib-1.0.1/lib/python3.8/site-packages/pathlib.py", line 643, in _make_child
python3.8-migra>         drv, root, parts = self._parse_args(args)
python3.8-migra>       File "/nix/store/sx1bdyn5hv9wlq2x2jw72qx1vmbzcgdm-python3.8-pathlib-1.0.1/lib/python3.8/site-packages/pathlib.py", line 606, in _parse_args
python3.8-migra>         return cls._flavour.parse_parts(parts)
python3.8-migra>       File "/nix/store/sx1bdyn5hv9wlq2x2jw72qx1vmbzcgdm-python3.8-pathlib-1.0.1/lib/python3.8/site-packages/pathlib.py", line 93, in parse_parts
python3.8-migra>         parsed.append(intern(rel))
python3.8-migra>     TypeError: can't intern String
python3.8-migra>     ----------------------------------------
python3.8-migra> ERROR: Command errored out with exit status 1: /nix/store/yl69v76azrz4daiqksrhb8nnmdiqdjg9-python3-3.8.8/bin/python3.8 /nix/store/74jp1w5s9n45bx7zq6avk5w7n5xq8wlq-python3.8-pip-20.2.4/lib/python3.8/site-packages/pip/_vendor/pep517/_in_process.py prepare_metadata_for_build_wheel /build/tmpme3inu0g Check the logs for full command output.
python3.8-migra>

I can run a development shell, although it prints a different error, like this:

$ nix develop
Executing pipShellHook
ERROR: File "setup.py" not found. Directory cannot be installed in editable mode: /home/tad/Projects/migra
(A "pyproject.toml" file was found, but editable mode currently requires a setup.py based build.)
WARNING: You are using pip version 20.2.4; however, version 21.0.1 is available.
You should consider upgrading via the '/nix/store/yl69v76azrz4daiqksrhb8nnmdiqdjg9-python3-3.8.8/bin/python3.8 -m pip install --upgrade pip' command.
Finished executing pipShellHook

Despite the error, it gives me a shell with poetry and python in the environment. So inside this shell I can reproduce the can't intern String error with more details:

$ poetry build -vvv

  Stack trace:

  13  /nix/store/ji2xd46pl09ga24a2b5v2c143zhih7km-python3.8-clikit-0.6.2/lib/python3.8/site-packages/clikit/console_application.py:131 in run
       129│             parsed_args = resolved_command.args
       130│
     → 131│             status_code = command.handle(parsed_args, io)
       132│         except KeyboardInterrupt:
       133│             status_code = 1

  12  /nix/store/ji2xd46pl09ga24a2b5v2c143zhih7km-python3.8-clikit-0.6.2/lib/python3.8/site-packages/clikit/api/command/command.py:120 in handle
       118│     def handle(self, args, io):  # type: (Args, IO) -> int
       119│         try:
     → 120│             status_code = self._do_handle(args, io)
       121│         except KeyboardInterrupt:
       122│             if io.is_debug():

  11  /nix/store/ji2xd46pl09ga24a2b5v2c143zhih7km-python3.8-clikit-0.6.2/lib/python3.8/site-packages/clikit/api/command/command.py:163 in _do_handle
       161│         if self._dispatcher and self._dispatcher.has_listeners(PRE_HANDLE):
       162│             event = PreHandleEvent(args, io, self)
     → 163│             self._dispatcher.dispatch(PRE_HANDLE, event)
       164│
       165│             if event.is_handled():

  10  /nix/store/ji2xd46pl09ga24a2b5v2c143zhih7km-python3.8-clikit-0.6.2/lib/python3.8/site-packages/clikit/api/event/event_dispatcher.py:22 in dispatch
        20│
        21│         if listeners:
     →  22│             self._do_dispatch(listeners, event_name, event)
        23│
        24│         return event

   9  /nix/store/ji2xd46pl09ga24a2b5v2c143zhih7km-python3.8-clikit-0.6.2/lib/python3.8/site-packages/clikit/api/event/event_dispatcher.py:89 in _do_dispatch
        87│                 break
        88│
     →  89│             listener(event, event_name, self)
        90│
        91│     def _sort_listeners(self, event_name):  # type: (str) -> None

   8  /nix/store/y41kng214n2xc9mrs4ayq152wf60j3wc-python3.8-poetry-1.1.5/lib/python3.8/site-packages/poetry/console/config/application_config.py:116 in set_env
       114│
       115│         io = event.io
     → 116│         poetry = command.poetry
       117│
       118│         env_manager = EnvManager(poetry)

   7  /nix/store/y41kng214n2xc9mrs4ayq152wf60j3wc-python3.8-poetry-1.1.5/lib/python3.8/site-packages/poetry/console/commands/command.py:10 in poetry
        8│     @property
        9│     def poetry(self):
     → 10│         return self.application.poetry
       11│
       12│     def reset_poetry(self):  # type: () -> None

   6  /nix/store/y41kng214n2xc9mrs4ayq152wf60j3wc-python3.8-poetry-1.1.5/lib/python3.8/site-packages/poetry/console/application.py:69 in poetry
        67│             return self._poetry
        68│
     →  69│         self._poetry = Factory().create_poetry(Path.cwd())
        70│
        71│         return self._poetry

   5  /nix/store/y41kng214n2xc9mrs4ayq152wf60j3wc-python3.8-poetry-1.1.5/lib/python3.8/site-packages/poetry/factory.py:33 in create_poetry
        31│             io = NullIO()
        32│
     →  33│         base_poetry = super(Factory, self).create_poetry(cwd)
        34│
        35│         locker = Locker(

   4  /nix/store/88jrbsgwpv4yq6g2jijbp4mf9mmsn51g-python3.8-poetry-core-1.0.2/lib/python3.8/site-packages/poetry/core/factory.py:71 in create_poetry
        69│
        70│         if "readme" in local_config:
     →  71│             package.readme = Path(poetry_file.parent) / local_config["readme"]
        72│
        73│         if "platform" in local_config:

   3  /nix/store/sx1bdyn5hv9wlq2x2jw72qx1vmbzcgdm-python3.8-pathlib-1.0.1/lib/python3.8/site-packages/pathlib.py:853 in __truediv__
        851│
        852│     def __truediv__(self, key):
     →  853│         return self._make_child((key,))
        854│
        855│     def __rtruediv__(self, key):

   2  /nix/store/sx1bdyn5hv9wlq2x2jw72qx1vmbzcgdm-python3.8-pathlib-1.0.1/lib/python3.8/site-packages/pathlib.py:643 in _make_child
        641│
        642│     def _make_child(self, args):
     →  643│         drv, root, parts = self._parse_args(args)
        644│         drv, root, parts = self._flavour.join_parsed_parts(
        645│             self._drv, self._root, self._parts, drv, root, parts)

   1  /nix/store/sx1bdyn5hv9wlq2x2jw72qx1vmbzcgdm-python3.8-pathlib-1.0.1/lib/python3.8/site-packages/pathlib.py:606 in _parse_args
        604│                     "argument should be a path or str object, not %r"
        605│                     % type(a))
     →  606│         return cls._flavour.parse_parts(parts)
        607│
        608│     @classmethod

  TypeError

  can't intern String

  at /nix/store/sx1bdyn5hv9wlq2x2jw72qx1vmbzcgdm-python3.8-pathlib-1.0.1/lib/python3.8/site-packages/pathlib.py:93 in parse_parts
        89│                     if x and x != '.':
        90│                         parsed.append(intern(x))
        91│             else:
        92│                 if rel and rel != '.':
    →   93│                     parsed.append(intern(rel))
        94│             if drv or root:
        95│                 if not drv:
        96│                     # If no drive is present, try to find one in the previous
        97│                     # parts. This makes the result of parsing e.g

I found some references to this error, but couldn’t figure out what to do next.

Side note: Previously I tried to get migra to work in my project using mach-nix, but there was another issue: https://github.com/DavHau/mach-nix/issues/254. I’m not sure how related they are. It is also about pathlib, so maybe? Anyway, I would prefer to make migra a flake in it’s own right than to use mach-nix, if only for my own leaerning.

Any help will be greatly appreciated.

1 Like

Would addressing this warning help?


warning: Git tree '/home/tad/Projects/migra' is dirty

I think that this is usually harmless. Flakes will ignore any files not known to your version control system, so you sometimes get confusing messages about a file not being found even though it is plainly in the directory, if you haven’t registered it with your VCS.

So, if you are using Git, it is enough to stage a file (without even committing it) to make the flake start noticing it. You then get that warning (until you commit, revert, or stash, whatever is making your tree dirty), but all it’s telling you (IIANM) is that the flake behaviour you are seeing is different from the committed version.

1 Like

Yes, that’s exactly what’s happening. I’m tweaking the code before committing, but the files are already staged, so this is not the problem. On a clean tree it fails the same way.

This sounds like a Poetry problem. Possibly related issue on Poetry’s GitHub (though it is outdated):
https://github.com/python-poetry/poetry/issues/2798

Thanks @i077. I saw this issue but can’t see how to apply it to my situation. In my pyproject.toml there are no exclude keys and the only thing that looks like a glob or path is README.md.

I made some new observations.

Observation 1: fresh pyproject.toml also doesn’t work

Recreating the pyproject.toml and poetry.lock without any dependencies doesn’t work:

$ nix develop

$ rm pyproject.toml poetry.lock
$ poetry init --no-interaction

This command will guide you through creating your pyproject.toml config.


You can specify a package in the following forms:
  - A single name (requests)
  - A name and a constraint (requests@^2.23.0)
  - A git url (git+https://github.com/python-poetry/poetry.git)
  - A git url with a revision (git+https://github.com/python-poetry/poetry.git#develop)
  - A file path (../my-package/my-package.whl)
  - A directory (../my-package/)
  - A url (https://example.com/packages/my-package-0.1.0.tar.gz)

$ poetry lock

  ValueError

  Invalid suffix ''

  at /nix/store/sx1bdyn5hv9wlq2x2jw72qx1vmbzcgdm-python3.8-pathlib-1.0.1/lib/python3.8/site-packages/pathlib.py:785 in with_suffix
       781│         """Return a new path with the file suffix changed (or added, if none)."""
       782│         # XXX if suffix is None, should the current suffix be removed?
       783│         drv, root, parts = self._flavour.parse_parts((suffix,))
       784│         if drv or root or len(parts) != 1:
    →  785│             raise ValueError("Invalid suffix %r" % (suffix))
       786│         suffix = parts[0]
       787│         if not suffix.startswith('.'):
       788│             raise ValueError("Invalid suffix %r" % (suffix))
       789│         name = self.name

If I add the readme field to [tool.poetry] like this:

--- pyproject.toml
+++ pyproject.toml
@@ -3,6 +3,7 @@
 version = "0.1.0"
 description = ""
 authors = ["Tad Lispy <tadeusz@lazurski.pl>"]
+readme = "README.md"
 
 [tool.poetry.dependencies]
 python = "^3.8"

I get the old error back:

$ poetry lock

  TypeError

  can't intern String

  at /nix/store/sx1bdyn5hv9wlq2x2jw72qx1vmbzcgdm-python3.8-pathlib-1.0.1/lib/python3.8/site-packages/pathlib.py:93 in parse_parts
        89│                     if x and x != '.':
        90│                         parsed.append(intern(x))
        91│             else:
        92│                 if rel and rel != '.':
    →   93│                     parsed.append(intern(rel))
        94│             if drv or root:
        95│                 if not drv:
        96│                     # If no drive is present, try to find one in the previous
        97│                     # parts. This makes the result of parsing e.g.

Setting readme = "./README.md" produces the Invalid suffix '' error again.

Observation 2: poetry from nixpkgs can build and run (!) migra

Like this:

$ nix shell nixpkgs#poetry

$ poetry build
Building migra (3.0)
  - Building sdist
  - Built migra-3.0.tar.gz
  - Building wheel
  - Built migra-3.0-py3-none-any.whl

$ poetry run migra
usage: migra [-h] [--unsafe] [--schema SCHEMA] [--exclude_schema EXCLUDE_SCHEMA]
             [--create-extensions-only] [--with-privileges] [--force-utf8]
             dburl_from dburl_target
migra: error: the following arguments are required: dburl_from, dburl_target

Notice that in this case I’m using nix shell with poetry instead of the development shell of the project (i.e. nix shell ... command instead of nix develop).

I really don’t know what to think about it.

{
  description = "Like diff but for Postgres schemas";

  inputs = {
    flake-compat = {
      url = "github:edolstra/flake-compat";
      flake = false;
    };
    flake-utils = {
      url = "github:numtide/flake-utils";
    };
    poetry2nix-src = {
      url = "github:nix-community/poetry2nix";
    };
  };

  outputs = { self, nixpkgs , flake-compat , flake-utils , poetry2nix-src }:
    flake-utils.lib.eachDefaultSystem (system:
      let
        pkgs = import nixpkgs {
          inherit system;
          overlays = [ poetry2nix-src.overlay ];
        };
      in rec {
        packages.migra = pkgs.poetry2nix.mkPoetryApplication {
          projectDir  = ./.;
          overrides = pkgs.poetry2nix.overrides.withDefaults (self: super: { 
            sqlbag = super.sqlbag.overridePythonAttrs (old: {
              propagatedBuildInputs = [self.packaging self.six self.sqlalchemy];
              postPatch = ''
                sed -i 's#pathlib#packaging#' setup.py
              '';
            });
          });
        };
        defaultPackage = packages.migra;
      }
    );
}

Almost there! Let me add a bit of context to the above.

Yesterday, as part of Nix Office hours, @tomberek helped me figure out why my builds are failing. Once again - huge thanks!

Turns out, that there was a problem with a dependency of migra called sqlbag. There was already an issue and a PR addressing the problem, but since it was not merged yet we had to apply a patch in the flake.

Patching sqlbag

This involves removing the pathlib from propagatedBuildInputs (so Nix won’t include it in build environment, which would lead to can't intern string error) and patching the setup.py so at runtime migra doesn’t expect pathlib to be available (at least that’s how I understand it - it’s complicated).

Using sed for patching seems dangerous to me, so I opted to use proper patch command. The input can be downloaded from the mentioned PR using this URL: https://github.com/djrobstep/sqlbag/pull/9.patch and has following contents:

From b9ba0504ffcbf08031da70b455162d0419494e16 Mon Sep 17 00:00:00 2001
From: Kai Groner <kai@gronr.com>
Date: Fri, 8 Jan 2021 13:50:29 -0500
Subject: [PATCH] make pathlib dependency conditional on python < 3

---
 setup.py | 5 ++++-
 1 file changed, 4 insertions(+), 1 deletion(-)

diff --git a/setup.py b/setup.py
index cc1d743..6fef632 100644
--- a/setup.py
+++ b/setup.py
@@ -15,7 +15,10 @@
     long_description=readme,
     author="Robert Lechte",
     author_email="robertlechte@gmail.com",
-    install_requires=["pathlib", "six", "sqlalchemy"],
+    install_requires=[
+        "pathlib; python_version<'3'",
+        "six",
+        "sqlalchemy"],
     zip_safe=False,
     packages=find_packages(),
     classifiers=["Development Status :: 3 - Alpha"],

I put it in nix/sqlbag-pathlib.patch file and modified the postPatch for sqlbag in flake.nix to look like this:

postPatch = ''
  patch --input=${./nix/sqlbag-pathlib.patch}
'';

This is sufficient to build and run migra!

$ nix build

$ result/bin/migra
usage: migra [-h] [--unsafe] [--schema SCHEMA] [--exclude_schema EXCLUDE_SCHEMA]
             [--create-extensions-only] [--with-privileges] [--force-utf8]
             dburl_from dburl_target
migra: error: the following arguments are required: dburl_from, dburl_target

Awesome! I pushed it to my fork at GitLab (nix branch) and now I can have migra in nix shell, even together with httpie!

$ nix shell gitlab:tad-lispy/migra/nix nixpkgs#httpie

$ migra
usage: migra [-h] [--unsafe] [--schema SCHEMA] [--exclude_schema EXCLUDE_SCHEMA]
             [--create-extensions-only] [--with-privileges] [--force-utf8]
             dburl_from dburl_target
migra: error: the following arguments are required: dburl_from, dburl_target

$ http 
usage: http [--json] [--form] [--multipart] [--boundary BOUNDARY] [--compress]
            [--pretty {all,colors,format,none}] [--style STYLE] [--unsorted] [--sorted]
            [--format-options FORMAT_OPTIONS] [--print WHAT] [--headers] [--body] [--verbose] [--all]
            [--history-print WHAT] [--stream] [--output FILE] [--download] [--continue] [--quiet]
            [--session SESSION_NAME_OR_PATH | --session-read-only SESSION_NAME_OR_PATH]
            [--auth USER[:PASS]] [--auth-type {basic,digest}] [--ignore-netrc] [--offline]
            [--proxy PROTOCOL:PROXY_URL] [--follow] [--max-redirects MAX_REDIRECTS]
            [--max-headers MAX_HEADERS] [--timeout SECONDS] [--check-status] [--path-as-is] [--chunked]
            [--verify VERIFY] [--ssl {ssl2.3,tls1,tls1.1,tls1.2}] [--ciphers CIPHERS] [--cert CERT]
            [--cert-key CERT_KEY] [--ignore-stdin] [--help] [--version] [--traceback]
            [--default-scheme DEFAULT_SCHEME] [--debug]
            [METHOD] URL [REQUEST_ITEM [REQUEST_ITEM ...]]
http: error: the following arguments are required: URL

So this covers one of the goals. The other goal is to be able to add migra to another project as a build dependency.

Using migra in another project

This unfortunately doesn’t work for me yet. Here is a flake from another project:

{
  description = "Flake with migra and httpie";

  inputs = {
    migra = {
      url = "gitlab:tad-lispy/migra/nix";
    };
  };

  outputs = { self, nixpkgs, migra }: {

    packages.x86_64-linux.with-migra = nixpkgs.legacyPackages.x86_64-linux.stdenv.mkDerivation {
      name = "with-migra";
      src = ./.;
      buildInputs = [
        migra
        nixpkgs.legacyPackages.x86_64-linux.httpie
      ];
    };

    defaultPackage.x86_64-linux = self.packages.x86_64-linux.with-migra;

  };
}

With this I have http but not migra:

$ nix develop

$ http
usage: http [--json] [--form] [--multipart] [--boundary BOUNDARY] [--compress]
            [--pretty {all,colors,format,none}] [--style STYLE] [--unsorted]
            [--sorted] [--format-options FORMAT_OPTIONS] [--print WHAT] [--headers]
            [--body] [--verbose] [--all] [--history-print WHAT] [--stream]
            [--output FILE] [--download] [--continue] [--quiet]
            [--session SESSION_NAME_OR_PATH | --session-read-only SESSION_NAME_OR_PATH]
            [--auth USER[:PASS]] [--auth-type {basic,digest}] [--ignore-netrc]
            [--offline] [--proxy PROTOCOL:PROXY_URL] [--follow]
            [--max-redirects MAX_REDIRECTS] [--max-headers MAX_HEADERS]
            [--timeout SECONDS] [--check-status] [--path-as-is] [--chunked]
            [--verify VERIFY] [--ssl {ssl2.3,tls1,tls1.1,tls1.2}] [--ciphers CIPHERS]
            [--cert CERT] [--cert-key CERT_KEY] [--ignore-stdin] [--help] [--version]
            [--traceback] [--default-scheme DEFAULT_SCHEME] [--debug]
            [METHOD] URL [REQUEST_ITEM [REQUEST_ITEM ...]]
http: error: the following arguments are required: URL

$ migra
migra: command not found

Why is it that I can run it with nix shell gitlab:tad-lispy/migra/nix but don’t have it in the development shell of the above flake?

By poking around I found out that this works, but it doesn’t seem right, does it?

{
  description = "A flake with Migra and Httpie";

  inputs = {
    flake-compat = {
      url = "github:edolstra/flake-compat";
      flake = false;
    };
    flake-utils = {
      url = "github:numtide/flake-utils";
    };
    migra = {
      url = "gitlab:tad-lispy/migra/nix";
    };
  };

  outputs = {
    self,
    nixpkgs,
    flake-compat,
    flake-utils,
    migra,
  }:

    flake-utils.lib.eachDefaultSystem (system:
      let
        pkgs = import nixpkgs { inherit system; };

      in
        {
          packages.with-migra =
            pkgs.stdenv.mkDerivation {
              name = "with-migra";
              src = ./.;
              buildInputs = [
                migra.defaultPackage.${system}
                pkgs.httpie
              ];
            };

          defaultPackage = self.packages.${system}.with-migra;
        }
    );
}

And with this I can:

$ nix develop
warning: Git tree '/home/tad/Projects/flake-with-migra' is dirty

$ migra
usage: migra [-h] [--unsafe] [--schema SCHEMA] [--exclude_schema EXCLUDE_SCHEMA] [--create-extensions-only] [--with-privileges] [--force-utf8] dburl_from dburl_target
migra: error: the following arguments are required: dburl_from, dburl_target

$ http
usage: http [--json] [--form] [--multipart] [--boundary BOUNDARY] [--compress] [--pretty {all,colors,format,none}] [--style STYLE] [--unsorted] [--sorted] [--format-options FORMAT_OPTIONS] [--print WHAT]
            [--headers] [--body] [--verbose] [--all] [--history-print WHAT] [--stream] [--output FILE] [--download] [--continue] [--quiet]
            [--session SESSION_NAME_OR_PATH | --session-read-only SESSION_NAME_OR_PATH] [--auth USER[:PASS]] [--auth-type {basic,digest}] [--ignore-netrc] [--offline] [--proxy PROTOCOL:PROXY_URL] [--follow]
            [--max-redirects MAX_REDIRECTS] [--max-headers MAX_HEADERS] [--timeout SECONDS] [--check-status] [--path-as-is] [--chunked] [--verify VERIFY] [--ssl {ssl2.3,tls1,tls1.1,tls1.2}] [--ciphers CIPHERS]
            [--cert CERT] [--cert-key CERT_KEY] [--ignore-stdin] [--help] [--version] [--traceback] [--default-scheme DEFAULT_SCHEME] [--debug]
            [METHOD] URL [REQUEST_ITEM [REQUEST_ITEM ...]]
http: error: the following arguments are required: URL