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鈥檓 a novice at both Nix and Python and I鈥檓 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鈥檝e added it:

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

Now I鈥檓 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鈥檛 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鈥檓 not sure how related they are. It is also about pathlib, so maybe? Anyway, I would prefer to make migra a flake in it鈥檚 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鈥檛 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鈥檚 telling you (IIANM) is that the flake behaviour you are seeing is different from the committed version.

1 Like

Yes, that鈥檚 exactly what鈥檚 happening. I鈥檓 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鈥檚 GitHub (though it is outdated):
https://github.com/python-poetry/poetry/issues/2798

Thanks @i077. I saw this issue but can鈥檛 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鈥檛 work

Recreating the pyproject.toml and poetry.lock without any dependencies doesn鈥檛 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鈥檓 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鈥檛 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鈥檛 include it in build environment, which would lead to can't intern string error) and patching the setup.py so at runtime migra doesn鈥檛 expect pathlib to be available (at least that鈥檚 how I understand it - it鈥檚 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鈥檛 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鈥檛 have it in the development shell of the above flake?

By poking around I found out that this works, but it doesn鈥檛 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