leed site push looks like git push with a longer name. It is not. A plain git push moves your commits to GitLab and stops there — no deployment record is created, no build is triggered, and your preview site keeps serving whatever it was serving before. The part that matters is the second half of the command: after the commits land, the CLI tells the CMS what you pushed, and that notification is what turns a commit into a site.
This page picks up where the command returns. The command itself — its checks, its refusals, the validate and commit steps that precede it — is documented at validate, commit and push.
The handoff, step by step
A push is two halves — commits to the remote, then the CMS told about them — and both finish before the command writes a single line. Here is the same sequence from the outside and from the inside.
- What you see
- What is happening
$ leed site push
Rebased 2 remote commit(s) onto your work.
Site pushed successfully...
CMS successfully notified!The rebase line appears only when the remote had moved since your last fetch. Site pushed successfully... reports git alone. The line that actually matters is the last one: CMS successfully notified! means a deployment record now exists and a build has started. Without it, your commits are on the remote and nothing is happening.
- Git identity check.
user.nameanduser.emailmust be set, because the commit attribution is read back later. - Branch check. Content is pushed from
stagingand nothing else. Any other branch is refused with “You are only allowed on specific branches. Change back to staging!”. - Anything to push? If
origin/stagingalready has everything you have, the command stops with “No changes have been committed. Nothing to push.” and exits0. - Credentials are re-validated with the server — always, not just when the local expiry looks stale. See below.
- Fetch and rebase. If the remote moved, your commits are replayed on top of it. There is no merge path; the flow is rebase-only.
- Push, then the tracked commit id is re-pointed at the post-rebase
HEAD. See below. - Notify the CMS —
POST /api/site/pushedwith the change set and the branch.
Why the token is always re-checked
Step 4 round-trips your credentials to the server on every push, even when the locally stored token has not expired yet. That looks wasteful and is not: a token can be revoked or rotated server-side while the copy on your disk still looks perfectly good.
The consequence of skipping it is the worst outcome this command has. Git would happily accept the push with your GitLab remote credentials, and then the notification — the half that creates the deployment — would be refused. Your work would be on origin/staging with nothing built from it, and nobody watching the Deploy screen would see a reason to look.
Why the commit id is re-synced after a rebase
A rebase rewrites your commits, so the sha recorded in the change set at leed site commit time no longer exists once step 5 has run. Step 6 re-points the tracked commit id at the actual pushed HEAD before the CMS is told anything.
If it did not, the CMS would create a deployment for an orphaned sha and the build would die at the very first thing Cloudflare does — checking out the commit — with an error that looks like a repository problem and is not.
What the CMS does with it
POST /api/site/pushed is not a webhook you have to configure; it is the command’s own second step. When it arrives, the CMS:
- creates a deployment row on the branch you pushed, carrying the single reason badge Developer CLI;
- marks any earlier deployment for the same commit as superseded, so a re-push of an unchanged commit does not leave two live rows competing;
- starts a build.
Attribution is worth a sentence, because the row shows your name even though the build itself runs on a server with no session. The CLI writes a structured payload into the commit message at leed site commit time, and the CMS reads the initiator out of that, falling back to the owner of the token that made the request. So a push made by CI on your behalf still credits whoever committed.
Why it lands on preview, not live
Every push lands on the preview site. This is not a setting and it is not a mistake.
Pushes go to the staging branch; staging is what builds the preview site; the live site changes only when a person promotes. The rule holds for everything that arrives as a file rather than as a CMS record — a CLI push, a save in the Layouts workspace, a managed-file refresh — and it is fixed in the product, not chosen per push. Why there are two sites at all.
Watching it
Open Deploy in the CMS rail (/deployments). Your row appears in the left-hand Preview site column with the blue Developer CLI badge, the seven-character commit hash, and your name. It moves through Building → Deploying → Active on its own; the screen re-fetches every five seconds while anything is in flight, so there is no reason to reload.
What each badge means, and why Active is the only one that tells you what visitors are being served, is covered in the blue reason badges and the status table beside it.
The build, from the outside
If you want to know what is actually running while that row says Building: Cloudflare starts a container, checks out your commit, installs the Leed build tooling, runs the site build, and uploads the result as a version of your site’s worker. Then it calls back into the CMS, and the CMS activates that version at 100% of traffic.
sequenceDiagram
autonumber
actor Dev as You (leed site push)
participant Git as GitLab (raw-content)
participant CMS as Leed CMS
participant CF as Cloudflare Builds
Dev->>CMS: re-validate CLI credentials
CMS-->>Dev: token still good
Dev->>Git: fetch + rebase onto origin/staging
Dev->>Git: push origin staging
Git-->>Dev: accepted
Dev->>CMS: POST /api/site/pushed (change set + branch)
CMS->>CMS: create deployment — Developer CLI, staging
CMS-->>Dev: CMS successfully notified!
CMS->>CF: trigger a build for that commit
CF->>Git: clone the commit
CF->>CF: leed site build
CF->>CF: wrangler versions upload
CF->>CMS: POST /api/site/build-result (version id)
CMS->>CF: activate that version at 100% of traffic
CF-->>CMS: deployment active
Note over CMS,CF: the preview site now serves your commit
The one step that surprises people is the last handoff. Uploading a version and activating it are two separate acts, and the CMS owns the second one. The build cannot put anything in front of a visitor by itself; it can only hand the CMS a version and report that it is ready. That separation is what lets a scheduled publish be built minutes early and still go live exactly on the chosen minute, and it is why a build can succeed while the site has not changed yet.
When it goes wrong
Three things can fail, at three different distances from you, and the symptom tells you which.
| Where it failed | What you see | Where to look |
|---|---|---|
| The CLI refused before pushing — wrong branch, nothing committed, a rebase conflict, or expired credentials | An error in your terminal and a non-zero exit; nothing reached the remote | The CLI’s own message — it names the remedy |
| The push landed but the CMS was never told | Site pushed successfully... followed by The CMS was not notified:, and no new row on the Deploy screen | Run leed site notify-cms again once the CMS is reachable |
| The CMS was told and the build failed | A red row in the Preview site column | Expand the row — the error block is the build’s own output |
The middle case is the one to recognize on sight, because the command reports both halves and it is easy to read the first line and stop. Your commits are on origin/staging and they are safe; what is missing is the deployment record. The recovery is the notification on its own, not another push — there is nothing left to push.
A red row is a build failure, and build failures have their own diagnosis path — the error block, whether the row offers Retry, and the handful of causes worth checking first are all in diagnosing a failed deployment. The exit codes the CLI reports, the branch rule and the exact rebase-conflict message are cataloged at CLI troubleshooting and exit codes.
Getting it live
When the row reads Active, your commit is on the preview site — staging. in front of your domain — and nowhere else. Review it there, on the real build rather than on your local server, because the preview site is the one running the same worker configuration your visitors will get.
Then promote. Push preview live on the Deploy screen releases everything currently on preview as a single live deployment; what promotion moves, and every reason the button is disabled.