Skip to content

Liber Tertius · Chapter 3 · Keeping a project current

Take one module fix mid-train

A patch release of one module does not need the whole project to move. The update stays inside the train the lock names, touches one module directory and one entry of the lock, and refuses as soon as a newer train is waiting.

≈5 min 5 steps A clean working tree A running stack
Source

Before you start

The module is one you required yourself. It has a line under requires in grim.json, and that line is the name you pass. A module that came in as a dependency of another cannot be moved alone.

Commit what you are working on. The update merges the new release into the module's directory under src/ and rewrites grim.lock, and it refuses to run over uncommitted changes to tracked files. A clean tree also keeps the commit of step four down to the fix.

Bring the stack up. A single-module update runs nothing in the container, and the migrations and the suite of the later steps need it.

$ git status --short
$ grim up

The ritual, in order

Five steps. Only the second changes the project, and the fifth is repeated on every other machine that has the project.

  1. Read the plan

    Nothing is written. The dry run of grim update with a module name checks that the module can be had, looks for a newer train, and prints the one row that would move.

    $ grim update cart --dry-run
    Checking for updates...
    
    Train 2.4 (core 2.4.2 → 2.4.2)
    
      cart                       2.4.0 → 2.4.1  (~2.4)
    
    Dry run — no changes applied.

    The row reads: the module, the version in the lock, the newest release on train 2.4 that the constraint in brackets allows. Everything is up to date on train 2.4. in its place means the fix has no release on your train yet.

    The headline names core with the same version on both sides of the arrow. That is how a single-module plan says core stays where it is. No core row in the table means no core change.

  2. Take the fix

    The same plan, applied. The new release is merged into src/Cart/ against the one you installed, so edits of your own in the module survive, and grim.lock is written again.

    $ grim update cart
    …
    Applying updates...
    
    ✓ 1 item(s) updated
    …
      cart 2.4.0 → 2.4.1: 3 written, 0 removed, 0 merged, 0 conflict(s)
    
    Review with git status and git diff, then commit it as:
      chore(grim): cart 2.4.0 → 2.4.1

    Core and the other units are not touched, and neither composer install nor the migrations run. In the lock, the entry of cart gets the new version and commit. Every other module, every theme, the train and the core version are carried over as they were.

    When you had edited a file the fix also changes, the merge leaves conflict markers in it, lists it under conflict(s) and exits non-zero, as git merge does. Resolve the markers before step four.

  3. Run the module's migrations

    Optional. A fix that ships a migration needs it applied by hand, because a single-module update leaves the container alone. grim artisan runs it in the app container.

    $ grim artisan migrate

    Nothing to migrate. is the normal answer for a fix that only changed code.

  4. Test, then commit the lock

    The project tracks the module as plain files, so the fix is in its diff next to the lock. Commit both once grim test agrees.

    $ grim test
    $ git status --short
    $ git add grim.lock src/Cart
    $ git commit -m "chore(grim): cart 2.4.0 → 2.4.1"

    The status lists files under src/Cart/ and grim.lock, and nothing else. In the lock, one entry has a new version and commit.

  5. Land it on the other machines

    A colleague gets the fix from your commit, not from running the update again. git pull brings the module's files and the lock together, at the release you tested. Only the migrations are left, because nothing runs them on pull.

    $ git pull
    $ grim artisan migrate

    A newer release published in the meantime changes nothing: the commit is what lands. grim install leaves a module directory that is already in the project as it is.

What you have now

One module and its entry in the lock moved. The train and everything else on it are the same as before.

{
    "train": "2.4",
    "core_version": "2.4.2",
    "modules": {
        "cart": { "version": "2.4.1" }
    }
}

Nothing is deployed yet. The fix reaches a server the way every change does, as a new image built from this commit.

When it does not work

“Train 2.5 available; run grim update (train)”

The core constraint in grim.json allows a newer train than the one in the lock, and a module is never moved alone across that line. Either take the whole train with Move a project to a new train, or pin the project to its train with "core": "2.4.*" and run the single-module update again. The pin is a decision to stay behind, so write it down in the commit.

“Module "payments" not found in grim.json”

The name is not under requires. Either it is spelled differently there, and only the spelling of grim.json is accepted, or the module is a dependency of another one. grim why shows which. A dependency moves with a bare grim update: while no newer train exists, that brings every unit to its newest release on the current train. Read its dry run first.

“grim.lock does not record a train”

The lock was written before trains existed, so there is no train to stay inside. Run grim update --dry-run and then grim update once. The lock it writes names the train, and single-module updates work from then on.

The suite fails with the fix in

Until you commit, the old release is still in git. The tree was clean when the update started, so restoring it undoes the update.

$ git checkout .
$ git status --short

src/Cart/ and grim.lock are back at the old release. Files the fix added stay behind as untracked, and the status lists them for you to delete. A migration the fix applied is not rolled back, so look at what step three ran before you go on.