Validate, Commit and Push

Shipping a template change is one workflow, always in the same order, and every step in it is enforced rather than recommended. A successful push produces one specific thing: a deployment on your preview site, badged Developer CLI. It does not touch your live site, and no CLI command ever will.

The four steps at a glance

  1. leed site validate — confirms every file you changed is one you are allowed to change, and records that it checked.
  2. leed site build — a plain build, so what gets committed is something a person has actually seen render.
  3. leed site commit -m "…" — stages everything and commits through the CLI, which is the only commit this repository accepts.
  4. leed site push — revalidates your session, rebases if the remote moved, pushes, and tells the CMS.
flowchart TD
    edit([You edited files]) --> validate[leed site validate]
    validate --> anyChanges{Anything modified?}
    anyChanges -->|no| nothing[No files were modified. Nothing to do.]
    anyChanges -->|yes| restricted{Any restricted file touched?}
    restricted -->|yes| reset[Refused — run leed site validate --reset]
    restricted -->|no| recorded[Validation recorded]
    recorded --> build[leed site build — no -d]
    build --> commit[leed site commit -m]
    commit --> identity{git user.name and user.email set?}
    identity -->|no| setIdentity[Refused — set them with git config --global]
    identity -->|yes| branch{On the staging branch?}
    branch -->|no| wrongBranch[Refused — git checkout staging]
    branch -->|yes| unvalidated{Every changed file validated?}
    unvalidated -->|no| runValidate[Refused — run leed site validate]
    unvalidated -->|yes| newer{Build newer than the newest validated edit?}
    newer -->|no| runBuild[Refused — run leed site build]
    newer -->|yes| committed([Commit created])
    committed --> push[leed site push]
    push --> ahead{Anything ahead of origin/staging?}
    ahead -->|no| exit0[Nothing to push — exits 0]
    ahead -->|yes| auth{Session still valid on the server?}
    auth -->|no| relogin[Interactive login, mid-command]
    auth -->|yes| fetch[git fetch origin staging]
    relogin --> fetch
    fetch --> behind{Remote moved?}
    behind -->|yes| rebase{Rebase cleanly?}
    behind -->|no| doPush[git push origin staging]
    rebase -->|no| conflict[Rebase aborted — resolve by hand]
    rebase -->|yes| doPush
    doPush --> notify[Tell the CMS what was pushed]
    notify --> deployment([Developer CLI deployment on staging])

Every “why won’t it commit” question is a node on that diagram. The checks are ordered and they short-circuit, so the first one that fails is the only message you see.

Step 1 — validate

leed site validate

Validation compares your working tree against the repository’s rules and refuses anything it is not willing to let you commit. In the reader’s terms, you may not:

  • change anything under src/_data/;
  • change anything inside a page type’s own folder — those are CMS-owned, and the list is read live from src/_data/pageTypeList.json, so it grows as you add page types;
  • change a read-only file: identity.json, .gitignore, any *.11tydata.json, src/static/images/favicon-*, src/static/images/logo-*, README.md, bunfig.toml, or anything under .ci/;
  • add a blocked file type: mov, mpg, avi, mp4, mp3, wav, pdf, xls, xlsx, doc, docx, ppt, pptx, zip, ttf, otf, ps;
  • commit a file 500 KB or larger.

One exemption is worth knowing because it looks like an inconsistency: .hbs files are exempt from the path and pattern rules. A Handlebars template inside a page-type folder is yours to edit; a Markdown page in the same folder is not.

Two file types people expect to be blocked are not: woff and woff2 are absent from the blocked list, so web fonts are fine. And validation does not check your front matter — nothing in validate inspects the contents of a page.

If validation lists files you should not have touched, put them all back in one step:

leed site validate --reset            # asks first
leed site validate --reset --dry-run  # list what a reset would restore, change nothing

Validation records which files passed. Edit one of them again and you validate again — the record is per file, not per run. Every rule, pattern by pattern, is in file validation rules; which paths are yours and which Leed restores on sight is in editable and read-only files.

Step 2 — build

leed site build

A plain build. Not --debug.

Commit compares the timestamp of your last recorded successful build against the newest validated edit, and a --debug build is deliberately never recorded, because unminified output is not what ships. Skip this step, or run it with -d, and commit refuses — with a message that tells you to build, which you will be certain you just did. This is the single most common false alarm on this page; the reasoning is in local development.

A --serve build does count, and records itself the moment its initial build completes. So an afternoon spent in the preview loop leaves you ready to commit without a separate build.

Step 3 — commit

leed site commit -m "Refresh the footer layout"

There is no git add step — the CLI stages every modified file itself, after the checks pass. -m is mandatory. Commits are accepted only on the staging branch, which is where leed site init left you.

To see what would go in without committing:

leed site commit --dry-run -m "Refresh the footer layout"

Six gates run in this order, and the first failure stops the command:

#GateFailure messageFix
1Git identity“Run the following commands to complete your setup:” followed by the two git config --global linesSet user.name and user.email
2Branch“You are only allowed on specific branches. Change back to staging!”git checkout staging
3Anything modified“No files were modified. Nothing to do.”Nothing to commit
4Restricted files“Run the following command to automatically remove and reset any restricted files:”leed site validate --reset
5Everything validated“You must run the following command before changes can be committed:” then the file listleed site validate
6Build newer than the newest edit“You must run the following command before changes can committed:”leed site build — plain, no -d

Gate 6’s message contains a typo in the shipped CLI. It is quoted verbatim in the troubleshooting table on CLI troubleshooting and exit codes, because that is the string people paste into search.

On success you get:

	Changes were committed. You may now push your changes:

		leed site push

You can commit as many times as you like before pushing; each commit repeats the checks against your latest edits.

What your commit message actually looks like

Open git log after a commit and you will find this, not your sentence:

commit 4f1a9c7d3b2e5a8091c6f4e7d0a3b5c8e2f19d64
Author: You <you@example.com>
Date:   Wed Sep 2 10:14:22 2026 +0000

    @leed/site-management=>{"message":"Refresh the footer layout","userId":"usr_a1b2c3","version":"4.105.0"}

Nothing is broken. The CLI wraps your text in a structured payload so the CMS can read two things back off the commit: the userId, which is how a deployment gets attributed to you rather than to whichever token happened to make the request, and the CLI version, which is how a build failure gets traced to a release. Your text is the message field, and it is what the CMS shows.

The repository’s commit-msg hook checks for exactly this shape. A commit message without it is rejected — which is the same mechanism that stops a bare git commit, described below.

Step 4 — push

leed site push

In order: it confirms your git identity, refuses any branch but staging, and checks whether you have anything the remote does not.

Then it revalidates your session against the server, unconditionally, even when the locally stored token looks healthy. A push using a revoked session would move your commits and then fail to notify the CMS, leaving work on the remote with nothing built from it. If the check fails, the command drops straight into an interactive login and carries on. That is why a push sometimes asks you to sign in halfway through; the reasoning is in CLI authentication.

After that: fetch, rebase if the remote moved, push, re-point the tracked commit id at the post-rebase HEAD, and notify the CMS. A successful run ends with CMS successfully notified! — that line, not the push line, is the one that means a deployment exists.

If the CMS is unreachable or refuses at that last step, the command reports “The CMS was not notified:” with the reason underneath, and its remedy names leed site notify-cms rather than another push. Pushing again will not help: with nothing new ahead of the remote, push stops at “Nothing to push” and never reaches the notification.

Rebase, never merge

Your repository takes commits from more than one place. The CMS writes published pages and data files into it; teammates push template changes. So the remote moves under you routinely, and leed site push handles it: if origin/staging is ahead, your commits are replayed on top of it before the push.

On a genuine conflict the rebase is aborted — your working tree is left as it was — and you get:

Remote has conflicting changes that can't be auto-rebased.
Run `git pull --rebase` in the content repo, resolve conflicts, then retry `leed site push`.

Which is exactly what to do:

cd raw-content
git pull --rebase
# resolve the conflicted files, then:
git add <resolved files>
git rebase --continue
leed site push

The git-side view of all this — who else writes to the repository and why linear history is a requirement rather than a preference — is in git workflow.

Why the CLI and not raw git

Three git commands are replaced. Everything else you know still works.

Instead ofUseBecauseEnforced by
git addnothingThe CLI stages everything itself, after the checks pass—
git commitleed site commit -m "…"Runs the six gates and writes the structured message the CMS readspre-commit and commit-msg hooks, exit 18
git pushleed site pushRevalidates the session, rebases safely, and notifies the CMS — a raw push notifies nothingpre-push hook, exit 18

This is enforcement, not etiquette. leed site init installs husky hooks that check for a leed.invoked git setting, which only the CLI sets. A bare git commit gets:

	You cannot 'commit' directly, please use:

		leed site commit -m "update details"

and the process exits 18. git push gets the same treatment with leed site push, and a commit message that is not CLI-shaped fails the commit-msg hook the same way.

git status, git log, git diff, git pull --rebase, git branch, git stash — all completely normal. It is only the three commands that write to the remote or to history that are routed through the CLI.

Flag-level detail for all three commands is on leed site validate, commit and push, and exit 18 sits alongside every other code the CLI can return in CLI troubleshooting and exit codes.

What happens after the push

The notification creates a deployment on staging with a single reason badge, Developer CLI, and starts a build. When that build finishes, your preview site is serving your commit.

A Developer CLI deployment row on the staging branch, expanded to show the commit hash and the attributed author

Promoting that to your live site is a deliberate act taken in the CMS. There is no --production flag, no branch you can push to instead, and no CLI command that does it. The full sequence from the notification to an activated version is in what happens after you push; the promotion itself is in promoting preview to live. If the build that follows fails, the message the CMS shows you is decoded in when a deployment fails.

validate, commit and push are three commands out of the CLI’s full tree, which is indexed with each one’s flags at CLI command reference.

ESC