Repos, repos, repos #2446
Description
Making changes in js-ipfs
typically involves changing this repo, the ipfs-http-client
, interface-js-ipfs-core
and also ipfds-ctl
(because of it's dependency on ipfs-http-client
.
We have these as separate modules but they are really modular in name only due to the cross-dependencies indicated above.
This has had a number of consequences:
- Developers have to coordinate PRs across all of these repos which is painful for experienced contributors and completely bewildering for new contributors
- It's impossible to have one PR that encapsulates a changeset - this makes it hard to test, hard to review, hard to integrate, hard to release and hard to roll back
- There is no one build that says "Yes, this change is ok" which results in a lot of manual work and makes automation harder because you have to juggle multiple versions of multiple modules
- There is a large amount of duplication across these repos, from tests to utility methods (
js-ipfs-utils
is helping with the utility methods but is also compounding the problem). - Coordinating shared dependency updates across these modules becomes a Byzantine task (e.g. see how out of date libp2p, ipld, are at the moment etc)
Since this project does not include the libp2p and IPLD codebases it should be a relatively thin layer on top of them, yet development is very complex.
We've started talking about re-integrating some core modules in #2222 but we should go further than that.
⚒️ Break the ipfsd-ctl
/ipfs-http-client
dependency
Possible solutions:
a. Make ipfs-http-client
a peer dependency of ipfsd-ctl
b. Require an implementation to be passed in the same way as with in-proc js-ipfs
daemons
💍 One PR to rule them all
Combine the repos. One way to do this might be adding the client/ipfsd to core:
const IPFS = require('ipfs')
const IPFSClient = require('ipfs/client')
...
✅ Pro: Simple to use
✅ Pro: Works with existing unspecified tooling that assumes one-module-per-repo
❌ Con: If you only consume the client you'll end up installing lots of dependencies you don't need
❌ Con: Separate PRs to interface-js-ipfs-core
still required unless you add that in too
❌ Con: Poor separation of concerns
❌ Con: All-or-nothing releases
Another way might be a lerna-style mono-repo a la babel:
$ ls -l
js-ipfs/
├── package.json
├── lerna.json
└── packages/
├── ipfs
├── ipfsd-ctl
├── ipfs-http-client
└── interface-js-ipfs-core
✅ Pro: One change, one PR
✅ Pro: Existing dependant code does not break because module names do not change
✅ Pro: Still able to release individual modules independently
✅ Pro: Easier to make, review and release changes
✅ Pro: Easier to contribute to
✅ Pro: The tooling figures out cross-package dependencies, no more npm link
required
✅ Pro: Clever enough to figure out which package depends on which and only test/release what's changed
❌ Con: In the worst case tests take as long as the slowest module
❌ Con: Cannot group GH issues by package
❌ Con: Tools that assume one-module-per-repo will need fixing
❌ Con: When adding new dependencies, it's easy to forget to add them to the right package due to dependency hoisting
cc @ipfs/wg-js-core and @ipfs/repos-javascript