The site updates itself after git push
When a Hugo, Astro or other generator site is built and uploaded by hand, the site may end up on the wrong version. How to publish it automatically from GitHub Actions, GitLab CI, Gitea or Forgejo.
When a generator-built site is built and uploaded by hand, fixes pile up, and the site may end up running a build that is not in the repository.
The fix is publishing from CI: whatever is on the main branch is on the site. A token that can only publish one site makes it safe.
What you get: every push to main reaches the site as soon as the CI job finishes; no more
manual uploads.
You need: a site created with us, a repository on
GitHub, GitLab, Gitea or Forgejo, and a working build command. Time: 10 minutes.
1. Issue a deploy token
Section titled “1. Issue a deploy token”In the panel open the site with Manage, block Deploy tokens (CI) → Create token. Copy it right away: the full value is shown once.
The token can only upload files and publish this site. Site settings, the domain, DNS and other sites are out of its reach, so a leak from CI costs little — and you can revoke it in the same place with one click.
2. Put the token into repository secrets
Section titled “2. Put the token into repository secrets”Name it WS_TOKEN: that is where the CLI looks without extra flags.
- GitHub, Gitea, Forgejo: Settings → Secrets → Actions → New secret.
- GitLab: Settings → CI/CD → Variables, with Masked and Protected ticked. The first keeps the token out of job logs, the second out of builds of other branches.
The token must never land in the repository itself, not even a private one.
3. Add a publish job
Section titled “3. Add a publish job”GitHub Actions, Gitea, Forgejo share the syntax:
# .github/workflows/deploy.yml (Gitea and Forgejo: .gitea/workflows/ or .forgejo/workflows/)name: Deploy siteon: push: branches: [main]
# Several pushes in a row — the last one publishes, the rest are cancelled.concurrency: group: deploy-site cancel-in-progress: true
jobs: deploy: runs-on: ubuntu-latest env: WS_TOKEN: ${{ secrets.WS_TOKEN }} steps: - uses: actions/checkout@v5 - run: npm ci && npm run build # your build → ./dist - name: Install CLI run: curl -fsSL https://raw.githubusercontent.com/webshieldpro/webshield-cli/main/install.sh | sh - name: Publish run: ~/.local/bin/webshield sites publish www.example.com --dir ./distGitLab CI:
deploy: image: node:24 rules: - if: $CI_COMMIT_BRANCH == "main" interruptible: true script: - npm ci && npm run build # your build → ./dist - curl -fsSL https://raw.githubusercontent.com/webshieldpro/webshield-cli/main/install.sh | sh - ~/.local/bin/webshield sites publish www.example.com --dir ./distSubstitute your own build command and output folder. Popular generators are listed in the
table: Hugo writes the site to
public/, Astro to dist/, MkDocs to site/. Hugo is not built with npm — replace
the build line, for example with the ready-made peaceiris/actions-hugo action and
hugo --minify.
4. Make sure the right thing gets published
Section titled “4. Make sure the right thing gets published”Before the first real run, look at the plan on your own machine:
WS_TOKEN=… webshield sites publish www.example.com --dir ./dist --dry-runThe CLI counts how many files it will upload, delete and keep as they are, and touches nothing. If the whole site is up for deletion, the folder is wrong.
Then push. The job log shows the same summary and a line saying the site was published.
How it works
Section titled “How it works”- Only the difference is uploaded. The CLI compares files with what we already hold and sends the changed ones. Editing one page uploads one file, not the whole site.
- Publishing is atomic. A visitor sees either the previous version or the new one in full — never old CSS mixed with new HTML.
- No cache to purge. Every publish is a new version, and the old cache does not apply to it.
- A failed publish does not stay stuck. If the files went up but publishing failed, the next run notices and publishes again.
curl -sI https://www.example.com/ | head -1The reply is HTTP/2 200, and the fresh change is on the site. If you don’t see it,
reload the page bypassing the browser cache (Ctrl+F5).
When something is off
Section titled “When something is off”| Symptom | Cause and fix |
|---|---|
| Authorization error | The secret does not reach the job: check env: WS_TOKEN, and in GitLab that the branch is protected — otherwise a Protected variable is not passed to it. |
| Site not found | The token was issued for another site, or the hostname in the command has a typo. A deploy token sees only its own site. |
| The summary deletes the whole site | --dir does not point at the build output, or the build failed earlier and the folder is empty. |
| The job succeeds, the site does not change | A different host is being published: compare the hostname in the command with the site name in the panel. |
.exe, .apk and the like are rejected |
Executables are available on commercial plans only. |
What next
Section titled “What next”- Command-line client — other commands and profiles.
- Redirects — the
_redirectsfile is published with the site and lives in the repository too. - A contact form without a backend.