Skip to content

Packages inside a project ​

Some projects carry their own packages: git repositories developed alongside the sites and used by them as composer packages (and, through vendor/, as npm packages). dqx clones them, checks your access to them, switches the sites between the released packages and your local copies, and shows them in the CLI, doctor, the dashboard and the pull request overview.

text
my-project/
├── .dq/config.yml
└── www/
    ├── shop.acme.dqx/              ← a site
    ├── admin.acme.dqx/             ← a site
    └── acme/
        ├── packages/core/          ← a package, its own repository
        └── modules/checkout/       ← a package, its own repository

Declaring them ​

Packages live in their own packages: list, next to sites:. A package belongs to the project, not to one site: every site that requires it uses the same checkout.

yaml
packages:
  - repository: acme-core            # a bare name or a URL, like repositories
    path: acme/packages/core         # under www/
    branch: development              # the default
    composer: acme/core              # ties the package to the sites that require it
  - repository: git@github.com:acme/checkout.git
    path: acme/modules/checkout
    composer: acme/checkout
    optional: true                   # no access skips it instead of stopping the install

A package path may not overlap a site checkout, a project repository or another package. Packages need a multi layout: a single-layout project has no www/ to hold them.

How a site uses the local copies ​

The site says how it installs against local packages, with what it already has. dqx runs it; it does not write composer files.

yaml
sites:
  - domain: shop.acme.dqx
    local:
      composer: composer-local.json   # COMPOSER=composer-local.json composer install
  - domain: admin.acme.dqx
    local:
      command: composer link-local    # or a command line of your own
      after: npm run install-dev      # optional, run after switching to local

composer is a file name in the checkout, usually one with path repositories and *@dev constraints, so vendor/acme/core becomes a symlink to the package. command and after are command lines run in the site's directory in its container, like hooks.postInstall. A site without local: always installs the released packages.

Packaged and local mode ​

yaml
install:
  packages: packaged    # or local: what dqx install sets up for everyone

dqx packages local and dqx packages packaged switch this project on your machine only. The choice is remembered per project, and the next dqx install keeps it. Switching runs composer in every site that has local:, then checks the result: in local mode every package a site requires must be a symlink into the package, in packaged mode a real directory. Anything else is reported per site and package. A package an installer plugin puts outside vendor/ (a module in Modules/<Name>) is looked for where vendor/composer/installed.json says it went.

A switch whose composer run fails keeps the mode you had, and says so: the sites may be half switched, and running that mode's command again puts them back. A switch to packaged runs the site's own composer.lock, so a lock that lags behind the local one fails here rather than in production.

Switching to local refuses while a required package is not cloned yet.

One site at a time ​

A project whose sites share packages can put one site on the local copies and leave the others on the released ones:

bash
dqx packages local --site app.acme.test     # app.acme.test uses the local checkouts
dqx packages                                # packages: mixed, with each site's mode
dqx packages packaged --site app.acme.test  # back to the project's mode

--site takes the primary domain or an alias of a site that declares local:; any other site is refused with the list of sites that can switch. Only that site's composer runs, with its own local: (a composer file or a command), and only its links are checked. The choice is remembered per site on your machine, next to the project's, and the next dqx install installs every site in its own mode. Switching the whole project (dqx packages local without --site) clears every site's own choice.

On the dashboard the Packages section switches with two buttons, local and packaged, the current one lit. Beside all sites they switch the whole project; a project with two or more sites that declare local: also lists them per site, each with its own pair. The section folds, and each card remembers how it was left; it stays open while it has an issue to show.

Install and access ​

dqx install checks access to the sites, the project repositories and the packages before cloning anything, and reports every repository you cannot read at once. It clones the project repositories, then the packages, then the sites, and runs composer in the mode that applies to each site: a fresh install follows install.packages (packaged when it is not set), a later one keeps the choices made on this machine.

dqx install --only packages and dqx packages clone clone only the packages.

Day to day ​

CommandWhat it does
dqx packagesEach package: branch (and the configured one when it differs), uncommitted work, ahead and behind, the sites using it
dqx packages pullFetch, then fast-forward every clean package on its own branch. A package with uncommitted work or a diverged branch is skipped, with the reason
dqx packages switch <branch> [package...]Put packages on a branch, for work that spans several. Refuses a package with uncommitted work
dqx packages cloneClone the packages that are not there yet
dqx packages scanFind repositories under www/ the config does not declare, and propose them

Nothing here resets a package to its configured branch: a package on a feature branch stays there until you move it.

Git hooks ​

Every package checkout gets the git hooks its site gets, the way local_install.sh hooked every clone under www/. A package rarely carries its own Pint or Prettier, so its hook runs the tools of the site that develops it: the one with a local: block, or else the first. The package's staged files are handed over by their path from that site (../acme/packages/core/src/...), and a package with its own pint.json is formatted by its own rules.

A package that states no hooks takes its site's. One that states its own keeps them, and its pre-commit falls back to the site's when it states none:

yaml
packages:
  - repository: acme-core
    path: acme/packages/core
    gitHooks: false                 # no hooks here; absent, it follows install.gitHooks
  - repository: acme-checkout
    path: acme/modules/checkout
    hooks:
      git:
        pre-push:
          - script: .dq/hooks/check-version.sh

dqx install, dqx githooks and dqx adopt all write them; dqx githooks --site leaves the packages out. See git hooks.

Adopting a project that already has packages ​

dqx adopt says when it finds repositories under www/ that are neither a site nor a project repository. dqx packages scan reads their origin, branch and composer name, matches them against the path repositories in each site's local composer file, and shows the packages: block it would add. It writes the config only after you confirm (or with --yes), and raises requires: to the release that reads packages, so a colleague on an older dqx is told to update instead of silently installing without them. A site that already has a local: block keeps it.

Elsewhere ​

  • dqx doctor reports a missing package, a package cloned from another repository than the configured one, a site not symlinked in local mode, and a package on another branch.
  • dqx sync reports remote drift for packages, as for sites.
  • Copies: with --worktree, dqx clone and dqx agent-env make the packages git worktrees of the original's, like the site checkouts.
  • Dashboard: the project card shows a Packages group with the mode, each package's branch and state, and buttons to switch mode and pull.
  • Pull requests: package repositories count as the project's repositories in the review overview.
  • MCP: dqx_project_info includes the packages, the mode and the link state.

dqx v1.78.0