Change a module and send it back
A module under src/ is a checkout of a released tag, and its repository is written by the release process alone. So a change does not go back the way it came. It travels to the monorepo as a pull request and returns to every project as a release.
Before you start
The stack is up and the suite passes, so a red test after your change is yours.
$ grim up
$ grim testSending the change needs the GitHub CLI gh, logged in, and write access to the monorepo the modules are released from. Reading it is enough for the dry run. A project that belongs to another organisation names its own monorepo in grim.json, and then that is where the pull request goes.
"upstream": { "repo": "acme/platform", "branch": "master" }The ritual, in order
Six steps. The fifth is the only one that leaves your machine, and the sixth happens days later, when the release is out.
See what you are about to change
Two facts decide whether the change can be sent at all. The module has to be a git checkout of its own, and it has to sit exactly on a released tag with nothing committed on top.
$ ls -d src/Cart/.git src/Cart/.git $ git -C src/Cart describe --tags --exact-match 2.5.0 $ git -C src/Cart status --shortA tag and an empty status are what you want. When
src/Cart/.gitdoes not exist, the module is part of core, which reaches the project as one copied tree. It cannot be sent this way. See When it does not work.Make the change and test it
Edit the files under
src/Cart/in place. The project runs the module from there, so the browser andgrim testsee the change at once.$ grim test --filter=CartTotalsTest $ grim testDo not commit inside
src/Cart/, and do not make a branch there. grim recognises the release you started from by the checkout'sHEAD, and one commit on top makes it unrecognisable. Your work stays in the working tree until it is sent. New files count as long as the module's own.gitignoredoes not exclude them.Check the manifest
Optional. Do it when you touched
module.json, for instance to require another module.grim manifest:validateapplies the rules the monorepo enforces on every pull request.$ grim manifest:validate src/CartOnly
~2.5passes as a constraint inrequires.^2.5and2.5.*are refused.Read what would be sent
The dry run of
grim pushfetches the monorepo into.grim/upstream/, finds the commit your installed release was cut from, and lays your working copy over it. Nothing is sent.$ grim push Cart -m "fix(cart): correct totals" --dry-run Pushing module Cart (2.5.0) to acme/platform master Fetching the monorepo (blobless, sparse on src/Cart)... OK (first clone) Looking for the base commit... 4f2a91c chore(release): train 2.5 fix(cart): correct totals src/Cart/Models/Cart.php | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) Dry run — nothing pushed.Read the file list. It has to be your change and nothing else, however far behind the newest train the project is. A debug file you forgot shows up here. Add
--patchto read the full diff.The title is yours to write, and grim suggests none. It becomes both the commit message and the pull request title, so it follows the monorepo's rules:
type(scope): subject, one line, at most 100 characters, a lowercase subject with no full stop. A title that breaks them is refused before anything is fetched.Open the pull request
The same command without the flag commits your change on the branch
grim-push/module/cartin the monorepo, pushes it and opens the pull request withgh.$ grim push Cart -m "fix(cart): correct totals" … Pushing grim-push/module/cart... OK Opening the pull request... OK https://github.com/acme/platform/pull/128One module travels per push. The branch name is fixed per module and the push is forced, so pushing
Cartagain rewrites the same pull request with the current state ofsrc/Cart/. That is how you answer a review. It also means a second, unrelated fix to the same module waits until the first is merged.Take the release when it arrives
The pull request is reviewed and merged in the monorepo, and the module gets a new release on your train. None of that happens on your machine. Until then your edits stay in
src/Cart/, and the project keeps running them. When the release is out, put your copy aside and take the released one withgrim update.$ git -C src/Cart stash -u $ grim update cart $ git -C src/Cart stash drop $ grim testThe stash comes first because the checkout of the new tag refuses to overwrite edited files.
-utakes the files you added along. Commit thegrim.lockthat the update wrote. Take one module fix mid-train covers this step on its own, including what to do when a newer train is already out.
What you have now
After step five, a pull request in the monorepo with a three-line body: the module, the release it was installed at, and the base commit. In the project, .grim/upstream/ holds a partial clone of the monorepo that later pushes reuse. It is ignored by git and safe to delete.
After step six, src/Cart/ is a clean checkout of the new tag, grim.lock names that version, and every other project gets the same fix with its next update. Until the release, your colleagues on this project do not have your change. It lives in your working tree only.
When it does not work
“Users is a core module”
The message goes on to say that core reaches a project as one copied tree, so there is no checkout to find a base with. grim push cannot send it. Make the change in a clone of the monorepo itself, under src/Users/ there, and open the pull request from that clone with git and gh.
“No commit on acme/platform master carries this exact module Cart.”
The checkout's HEAD matches no release in the monorepo. Nearly always you committed inside src/Cart/. Undo the commit and keep the changes, then push again.
$ git -C src/Cart reset --soft HEAD~1If it still fails, the installed tag is older than the search reaches. Stash the change, run grim update, apply the stash and push again.
“Commit message rejected (commitlint runs on the PR too):”
Each line under it names one rule the title broke, such as subject must not be sentence-case or upper-case — start it lowercase. Allowed types are build, chore, ci, docs, feat, fix, perf, refactor, revert, style and test.
“Your gh token is missing the scope(s) …”
That is not a permissions problem in the monorepo. The token gh holds lacks repo or read:packages. Run grim auth:login, which adds them, and push again.
The push succeeded and the pull request did not open
Opening the pull request... failed is followed by a compare URL. The branch is already upstream. Open the URL in a browser and create the pull request by hand.
“No changes in module Cart against its base — nothing to push.”
Your working copy equals the installed release. Either the edit went into another module, or the file you added is excluded by the module's .gitignore.
Questions
Can I send a theme the same way?
Yes. Name it with --theme instead of the module argument, as grim push --theme frontend/shop-front -m "…". Everything else in this recipe holds, with resources/themes/frontend/shop-front in place of src/Cart.
Can I send a module I made with grim make:module?
No. It has a repository of its own and no copy in the monorepo, so no base commit exists. Push it with git from src/<Name>/, as Create a module shows.
Next recipe
Create a module
Scaffold a module with its own repository, give it a table, check that it is enabled, write its first test, validate the manifest, publish a first version and add it to grim.json.
Read itSpells used here
grim push
Send a change upstream
Turn the edits you made to one installed module or theme into a pull request against the monorepo it is released from.
grim test
Run the test suite
Run the project's tests in the app container against a separate test database that grim creates and migrates for you.
grim manifest:validate
Check a manifest
Validate module.json, theme.json and core.json against the manifest schema on your machine, before CI does it for you.
grim update
Board the newest train
Move core, every module and every theme onto the newest release train grim.json allows, or a single module within the train it is on.
grim auth:login
Log grim in to GitHub
Give grim a GitHub token, from your gh login or a personal access token, and hand the same token to Composer and Docker.
Related recipes
Take one module fix mid-train
Read the one-row plan, move a single module to its newest release on the train the project is locked to, run its migrations, test, commit the lock and land it on the other machines.
Onboard as a collaborator
Get access to the packages, log grim in, clone the project, install what its lock records, bring the stack up and run the suite once.