How does one create opam switch for a multi-package project where some but not all packages are released?

I’m trying to create an opam switch for my ocaml project climate. This project has 3 packages: climate, climate_cmdliner, and climate_tests. Of these packages, only climate is released to opam. Before releasing climate I was able to create a local opam switch for this project with opam switch create . but now that climate is released this command no longer works:

 $ opam switch create . -y
Package climate_tests does not exist, create as a NEW package? [y/n] y
climate_tests is now pinned to git+file:///tmp/tmp.4t97xpWKwP/climate#main (version dev)
Package climate_cmdliner does not exist, create as a NEW package? [y/n] y
climate_cmdliner is now pinned to git+file:///tmp/tmp.4t97xpWKwP/climate#main (version dev)
climate is now pinned to git+file:///tmp/tmp.4t97xpWKwP/climate#main (version 0.2.0)

<><> Installing new switch packages <><><><><><><><><><><><><><><><><><><><><><>
Switch invariant: ["ocaml" {>= "4.05.0"}]
[ERROR] Could not determine which packages to install for this switch:
  * Missing dependency:
    - climate >= dev
    no matching version

It looks like opam has figured out that the climate package exists in the package repo and so it’s pinned it with version 0.2.0, but the other packages aren’t released so it pins them with version dev, and then in the non released packages it’s unable to solve the "climate" {= version} entries in their dependencies. I want opam to ignore the fact that climate happens to have been released and pin it with version dev instead. Is this possible?

I also tried creating an empty switch and explicitly pinning climate.dev:

$ opam switch create . --empty
$ opam pin climate.dev . --no-action
$ opam install . --deps-only
[climate.dev] synchronised (git+file:///tmp/tmp.4t97xpWKwP/climate#main)
[climate_cmdliner.~dev] synchronised (no changes)
[climate_tests.~dev] synchronised (no changes)
[ERROR] Package conflict!
  * Missing dependency:
    - deps-of-climate < dev
    no matching version

I’ve never seen this deps-of-climate faux package before. Is that new in opam.2.2.0? (I’m running opam.2.2.0 by the way.)

Ok I found a way to make it work but it’s not pretty. It works if I first create an empty switch, then pin all the local packages from the project, then install their dependencies:

$ opam switch create . --empty
$ opam pin climate.~dev . --no-action
$ opam pin climate_tests.~dev . --no-action
$ opam pin climate_cmdliner.~dev . --no-action
$ opam install . --deps-only

Is there a more concise way to do this with opam? It seems like multi-package projects are pretty common, as are local packages not intended for release, so I’d expect there to be an easy way to set up a dev environment for a project when only some of its packages have been released.

So the issue stems from a dependency "climate" {= version} and that opam will use the latest version number when pinning or dev when it’s unreleased. In your case climate is released as 0.2.0 but climate_tests and climate_cmdliner are unreleased and thus have version dev.

I think you can do opam switch create . --no-install to create the switch but ignore the local opam files and then do opam pins as you did. It basically boils down to the same…

Reading opam pin add --help I see it has an option --with-version that seems very helpful for pinning - this would allow you to do the pins in one command. Unfortunately, opam switch create doesn’t have that option.

I think the situation would be better if the opam files in the repo included a version field. Personally, I think this is a good thing always to have, but it’s especially useful if you’re using = version constraints. However, that doesn’t solve issues with other fields becoming out-of-date (upperbounds, etc.) over time.

e.g. the compiler’s:

As noted by @reynir, the --with-version argument would help here. This would reduce the number of commands by half in your case:

$ opam switch create . --empty
$ opam pin add . --with-version dev --no-action
$ opam install . --deps-only

We could try and think of a way to do this automatically. For example when pinning a repository with several package and one of them will use the default version, then all packages could use the default version too. Would you be able to open a ticket in the opam bugtracker?

Ah yes that simplifies things a bunch, thanks for the tip. Before I make an issue I want to properly understand the intention behind the current design. Specifically, I’d like to know why opam treats local packages (ie. packages with opam files in the current dir) differently depending on whether there exists a package with the same name in the opam repository? What is the intended use case of this feature?