Skip to content

Liber Tertius · Chapter 3 · Keeping a project current

Move a project to a new train

A new train moves core, every module and every theme at once. Most of the work is reading the plan before anything changes.

≈15 min 6 steps A clean working tree A running stack
Source

Before you start

Commit what you are working on. grim update refuses a dirty tree: it lands the new train as a diff in the working tree — grim.lock and the changed files of core and the modules — and that diff, mixed with your own changes, says nothing.

Bring the stack up. After a train change grim runs composer install and the migrations in the running app container. With the stack down it skips both and leaves them to you.

$ git status --short
$ grim up

The ritual, in order

Six steps. Only the fourth changes the project.

  1. Update grim itself

    The infrastructure templates of step five come from the grim on this machine, and so does the logic that decides what an update removes. Take the newest release first with grim self-update.

    $ grim self-update
  2. Read the plan

    Nothing is written. The dry run of grim update checks that every package can be installed, resolves the train, and prints what would move and what would be removed.

    $ grim update --dry-run
    
    Train 2.4 → 2.5 available (core 2.5.0)
    
      core                 2.4.2 → 2.5.0  (2.5.*)
      cart                 2.4.0 → 2.5.0  (~2.0)
      theme/admin:float    2.4.1 → 2.5.0  (~2.0)
    
        Would remove tests/Feature/LegacyHealthTest.php

    When it prints Everything is up to date on train 2.4. instead, the project is already on the newest train its core constraint allows. A constraint such as 2.4.* pins one train on purpose. Change it in grim.json to move on.

  3. Decide what the plan turned up

    Four kinds of line around the table need a person. Everything else is automatic.

    Line Decide
    Would remove … A file core dropped, unchanged in your project. It goes. If your code still uses it, copy what you need somewhere of your own now.
    Kept … modified locally Core dropped it, you had edited it, so it stays. Keep it as yours, or delete it by hand.
    … left core in train … A whole module is no longer part of core. Nothing is deleted. Add it to grim.json with grim require, or take it out with grim remove.
    ⚠ module … has no release on train … Everything else moves and that module stays where it is. Wait for its release, or go on without it and update it later.
  4. Board the train

    The same plan, applied. Every removed file is named as it goes, the new releases of core, the modules and the themes are merged into the tree, the lock is written, and the app container runs composer install and the migrations.

    $ grim update

    A file you edited that the new train changed too is merged three ways. A clash leaves conflict markers, the file is listed, and update exits non-zero: resolve the markers as after a git merge before you go on. Update refuses to start on uncommitted changes, which is why you committed first.

  5. Renew the infrastructure files

    grim update never touches the Docker files. They come from the templates of the grim you updated in step one, and grim upgrade writes them.

    $ grim upgrade
    $ grim down
    $ grim up

    The managed files are overwritten without a question, without a backup and without a preview. docker-compose.yml and the other files you own are left alone. The restart makes the containers read the new files. A managed file you once edited by hand is back to the template now, unless you track it with git add -f and can restore it from git. Directives of your own belong in docker/extra/, which is tracked and which upgrade never writes.

  6. Test, then commit the lock

    The project tracks core and every unit as plain files, so the update's diff is the train itself, and the lock is the record of it. Commit the whole result once grim test agrees, with the message update printed at its end.

    $ grim test
    $ git status --short
    $ git add -A
    $ git commit -m "chore(grim): core 2.4.2 → 2.5.0, cart 2.4.1 → 2.5.0"

What you have now

A lock that names the new train, and a project whose files match it, minus what core no longer ships.

{
    "train": "2.5",
    "core_version": "2.5.0",
    "modules": {
        "cart": { "version": "2.5.0" }
    }
}

Your colleagues get the same tree from git pull, and grim install brings their dependencies and database in line with it. Nothing is deployed yet. The train reaches a server the way every change does, as a new image.

When it does not work

A package cannot be installed

The update stops before it touches anything and lists every package it cannot have. An archived module usually says what replaced it. Often that is core itself, and then the module comes out of grim.json. A private one means your GitHub account lacks access to it.

“Cannot read the grim-core … tree”

Cannot read the grim-core 2.4.2 tree (…) — nothing removed. means grim could not look up the core release the lock names, typically with a lock written before trains existed. The train still landed. Nothing was deleted, so files core no longer ships stay in the project until you remove them by hand. The next update, from the lock this one wrote, removes normally.

“Environment is down”

Environment is down — run `grim up` to apply composer and migrations. means the stack was not running, so both were skipped. grim up alone does not catch up: on your machine it installs Composer packages only into an empty vendor/ and never migrates. Run the three commands.

$ grim up
$ grim composer install
$ grim artisan migrate

The suite fails on the new train

There is no downgrade command, and an older train gets no fixes. The way out is forward. Find the module the failure lives in with grim why, report it or fix it, and take the patch release when it lands, which is the recipe Take one module fix mid-train.

Do not edit versions in grim.lock by hand to hold one module back. grim ignores a locked version from another train, and a module from one train against the core of another is the breakage trains exist to prevent.