Managing PKP depencies with Composer

Alec, while discussing with @marc and @AndrewGearhart about implementation paths for Container packaging, we were circling around subjects adjacent to the deployment area, such as potential/ongoing Artisan and Flysystem migrations on the Laravel side, but also the initial bootstrap of the dependencies during an eventual build stage, which involves provisioning of the submodules, some included in the tarball, but eventually also other plugins.

Do you know if this step can be achieved alone with Composer, without involving submodules or manually copying tarball extracts into place? In other words: Can we install a whole OJS instance with all dependencies from a single composer.json?

Hi @jonr,

We have been discussing the possibility of moving to a monorepo approach to avoid some of the headaches involved in developing with submodules – primarily frequent merge conflicts with the lib/pkp submodule. But if we choose that path it’ll be a while before it can be realized.

Composer can deploy from git, but because the Composer config is in the lib/pkp submodule already, any attempt to use it to deploy the submodules would require moving it into the application repos, which would be a significant amount of config duplication. Unfortunately Composer configurations can’t be composed – at least not yet – so we wouldn’t be able to e.g. keep most of it in lib/pkp and refer to it from the application repo. (And I’d be cautious about adding separately-maintained Composer configs in several directories – it can lead to version conflicts, which we’ve already seen occasionally in the few plugins that have their own dependencies.)

Even if we could move the submodule checkouts into a Composer config, we’d still be left with the Node dependencies, which will continue to be managed where they should with npm. So I think for the foreseeable future we’ll have three main sources of dependencies:

  1. Composer (in lib/pkp and a few plugins)
  2. Node (in the application repo, lib/ui-library, and possibly plugins)
  3. Submodules

I actually think that submodules are very helpful for managing some of the built-in plugins – particularly the ones that are shared with the other applications. It’s lib/pkp where they give us headaches.

Sorry if that’s bad news, but hope that explanation is helpful!

Thanks,
Alec Smecher
Public Knowledge Project Team

1 Like

Thanks Alec for the quick and extended reply. I’ll need to wrap my head a little more around this. Any pointers towards being able to phase out the submodules, e.g. by some regard with Composer, will be highly appreciated. I guess Composer is also able to execute the NPM builds, if needed.

Hi @jonr,

Can you give me a little more context on the goals? Is it just to have a single command line to execute to get everything installed (i.e. wrapping everything in Composer, even if that results in executing npm, get, etc), or is it reducing the number of tools involved, or something else?

Thanks,
Alec Smecher
Public Knowledge Project Team

Hey @asmecher , thanks for the question.

Yes indeed, the rationale is to reduce the total amount of tools needed to compile a successful build. Getting the dependency on git esp. with its submodules out of the way and moving the handling of repositories or tarred releases of dependencies for development and production strictly into the preferred package manager of the language ecosystem of choice, here PHP.

This would make it easier to follow developments in other places of the community, given they also treat composer the same way, such as Laravel, Artisan, Eloquent or Flysystem, and to piggy bag from them.

Turning the suite of PKP and OJS artifacts into Composer packages might also bring other network effects with regards to adopting more recent PHP development patterns available since introduction of the v8 series, like type systems or more functional idioms around immutability and statelessness of methods.

If the Composer can be asked to also deal with orchestrating the whole suite of tools involved in development and building the application, e.g. using scripts, the better.

Composer can deploy from git, but because the Composer config is in the lib/pkp submodule already, any attempt to use it to deploy the submodules would require moving it into the application repos, which would be a significant amount of config duplication.

Maybe we can find a way that models nicely around common development and build usage patterns? It seems “falling back” to the convention of a package manager instead of raw git about how to define and reference dependencies seems like a possible risk to take?

What about invoking Composer commands in submodules through scripts in composer.json of the root repository in the meantime?

We have been discussing the possibility of moving to a monorepo approach to avoid some of the headaches involved in developing with submodules – primarily frequent merge conflicts with the lib/pkp submodule. But if we choose that path it’ll be a while before it can be realized.

What is blocking you from copying a working checkout into a single repository or why would that still take a while, if I may ask? You could try moving such an example over into a new repo, work from there for a while and evaluate your findings of the experiment against the original hypotheses to see, if they worked out?

Ideally I’m considering a flat tree structure like the following in dream code for defining a single OJS instance:

composer.json
defining:
├── ojs tag
├── lib tag
├── theme tag
└── plugin{s,…} tag

Turning OJS and the library into peer dependencies seems to avoid some of the nuisances you mention. Thus the dependencies of a complete instance will be, are and can be described in a single file, turning that into the single source of truth about the Software builds used for an instance.

Which in return nicely closes the circle to the original subject of the thread, allowing to computationally determine the SBOM: Unmarshalling semi-semantic JSON for attestation still brings more metadata than a raw .git and .gitmodules environment and better addressing of that metadata in its respective Composer namespace. The framework also provides us with guiding principles in shape of sufficient conditions to make working with it possible.

Consolidating on a single build tool orchestrating the other(s) may ultimately help achieve reproducibility with less layers of abstraction involved.

How do you think about this?

1 Like

Hi @jonr,

I’ll have to think about this – I have to admit I’m skeptical that Composer can really take on all those details. It may be the most common PHP dependency management tool, but I think what you’re proposing will be stretching it well out of its comfort zone.

We could certainly use it as a wrapper to execute other tools – I think that’s what you were proposing above e.g. for npm:

I guess Composer is also able to execute the NPM builds, if needed.

…right? And if not for npm, why not also for git?

Thanks,
Alec Smecher
Public Knowledge Project Team

Please, do take your time. It’ll be very convenient and useful for anyone else, when PKP with its core engine (pkp-lib) and layered applications (OJS, OPS, OMP) embraces the package and dependency manager of its language ecosystem (PHP) and doesn’t sheer out.

I’d always prefer the declarative approach to ensuring dependencies, also when loading from git, over scheduling an imperative script. Also there is first-class support for git in Composer. Why not use it?

https://getcomposer.org/doc/05-repositories.md#loading-a-package-from-a-vcs-repository

Hi all,

I did a quick experiment as follows…

  • Set up the lib/pkp submodule (pkp-lib repo) to see itself as a named library;
  • Required it using a new OJS Composer configuration via the git driver
  • Used the composer-installers-extender package to get it to install in the right place (lib/pkp rather than vendor)

This initially looks promising – it runs, and allows for the vast majority of dependencies to stay configured in pkp-lib. You can use the --prefer-source parameter to composer install to get Composer to check out using git – though that applies equally to all libraries (a considerable number!) and there doesn’t seem to be a way to set it per-package.

We would need to do this consistently for all submodules to be worthwhile, I think.

I’m still not sure how we’d treat versioning. I suppose we would use e.g. dev-stable-3_5_0 to cause OJS to check out the stable-3_5_0 HEAD for stable branches, and would manually set and commit a version number in package.json for builds.

This does still need some proving out – but it’s worth further work.

Branches:

Regards,
Alec Smecher
Public Knowledge Project Team

2 Likes

Thank you Alec for the quick experiment. This is very astonishing to observe, wherever we’ll be able to take this.