overleaf-paper-sync
overleaf-paper-sync
Description
Manage Overleaf-backed LaTeX papers mirrored to GitHub with bidirectional GitHub Actions sync. Use for Overleaf git tokens, paper version control, GitHub mirrors, divergence or merge conflicts between Overleaf and Git, repos with sync-overleaf/pull-from-overleaf workflows or sync helpers, collaborator onboarding, and named paper instances listed in references/instances.md.
SKILL.md
Overleaf Paper Sync
Manage a LaTeX paper that lives on Overleaf by mirroring it on GitHub, with both directions kept in sync automatically by two GitHub Actions workflows. The point is to get Overleaf's editor + preview while keeping GitHub's version control, PR review, and tooling — without paying for Overleaf's built-in GitHub integration.
The mental model
Overleaf is a great editor (live preview, real-time collab, browser-based). It's a weak version control system. GitHub is the inverse. This setup keeps Overleaf as the editor of record and GitHub as the source of version truth, connected by two automated paths:
GitHub → Overleaf (push trigger). Every push to GitHub's
masterruns.github/workflows/sync-overleaf.yml, whichgit pushes to Overleaf's git endpoint within seconds.Overleaf → GitHub (cron + on-demand).
.github/workflows/pull-from-overleaf.ymlruns hourly on a cron and on manual trigger. It fetches Overleaf, compares HEADs, and:- if Overleaf has new commits (a web edit), fast-forwards GitHub master onto them and pushes,
- if GitHub is already ahead, does nothing — the other workflow handles that direction,
- if both sides have unique commits, fails loudly with resolution instructions, because automated merges of LaTeX produce silently-broken papers.
Local clones also get a sync.ps1 / sync.sh helper that the user runs before each editing session: it triggers the pull workflow, waits for it to finish, and fast-forwards the local working copy. This catches collaborator web-edits without waiting up to an hour for the next cron tick.
That's the whole system. Everything else in this skill is one of: setup, conflict resolution, or troubleshooting.
Recognizing an already-wired-up paper
When stepping into an unfamiliar paper repo, these signals mean this skill applies:
.github/workflows/sync-overleaf.ymland/or.github/workflows/pull-from-overleaf.ymlexist- Two git remotes — one with a
git.overleaf.comURL (often namedorigin), one with agithub.comURL (often namedgithub) - A
sync.ps1/sync.shin the repo root - An
OVERLEAF_TOKENsecret configured on the GitHub repo
If 2+ of these are present, treat the repo as already set up — skip the setup section and go to Daily editing workflow below.
Daily editing workflow
This is the most-used section. Walk the user through it whenever they're about to work on the paper.
Step 1 — Before editing: pull Overleaf changes
From the paper repo root:
# Windows
.\sync.ps1
# Mac / Linux
./sync.sh
Takes ~10–30 seconds. It triggers the pull workflow on GitHub, waits for it, then fast-forwards the local clone. After this, the local working copy reflects whatever the latest collaborator web-edit on Overleaf left behind.
If the script reports "diverged" or the workflow run failed, see Conflict resolution below — don't keep editing on top of a divergent state.
Step 2 — Edit normally
VS Code, Cursor, vim, whatever. The paper repo is a regular git repo from here.
Step 3 — After editing: commit and push to GitHub only
git add .
git commit -m "<message>"
git push github master
The sync-overleaf workflow fires within seconds and pushes to Overleaf automatically. The user does not need to git push origin (Overleaf) — pushing manually is redundant and racy.
Sanity-check the workflow ran:
gh run list --workflow=sync-overleaf.yml --limit 1
What about commits made directly on Overleaf web?
Those reach GitHub via:
- The hourly cron of
pull-from-overleaf(worst case ~60 min latency — GitHub Actions cron is delayed by 5–15 min in practice on top of the schedule) - The user running
sync.ps1/sync.sh(fastest — usually under a minute) - A manual
gh workflow run pull-from-overleaf.yml
The commit on GitHub will be authored by github-actions[bot] since the workflow does the push — that's expected.
Conflict resolution
The pull-from-overleaf workflow is fast-forward only. If GitHub and Overleaf have each grown commits the other doesn't have, the workflow fails on purpose. Auto-merging LaTeX between two unrelated edits would produce subtly broken output that nobody notices until the figure is upside down in the final PDF.
Full playbook, including the exact commands to detect each case and recover, is in references/conflict-resolution.md. The short version:
cd <paper-repo>
git fetch origin master # origin = Overleaf
git fetch github master
git checkout master
git pull github master --ff-only # bring local up to GitHub
git merge origin/master # merge Overleaf in; resolve conflicts
git push github master # triggers sync-overleaf to push back to Overleaf
If the local clone doesn't have credentials cached for the Overleaf remote, fetching from origin will prompt. The username is the literal string git; the password is the OVERLEAF_TOKEN.
Setting up sync for a new paper
When the user wants to add this pattern to a paper that's currently only on Overleaf (or set up a new mirrored paper from scratch), follow the procedure in references/setup-new-paper.md. It covers:
- Generating an Overleaf git token (and the gotcha that the token's username must be the literal string
git, not the user's email — Overleaf changed this and the old "email + token" auth gives a 403) - Cloning the Overleaf project to a local working dir
- Creating an empty GitHub repo and pushing the contents up
- Setting the
OVERLEAF_TOKENsecret on the repo - Dropping the two workflow files and the
sync.ps1/sync.shhelper into place (templates are inassets/) - Optional: registering the paper repo as a submodule of a parent monorepo
The templates in assets/ use the literal string __OVERLEAF_PROJECT_ID__ everywhere the Overleaf project ID has to go — search-replace it once during setup.
Common errors and fixes
Quick reference. Full details and recovery commands in references/troubleshooting.md.
| Symptom | Likely cause | Fix |
|---|---|---|
403: Overleaf now only supports Git authentication tokens in workflow |
Workflow URL uses email as username | Use https://git:${TOKEN}@git.overleaf.com/<id> — literal git, not email |
fatal: Not possible to fast-forward in pull workflow |
Histories diverged | See conflict-resolution.md |
| Workflow can't push to GitHub master | Missing contents: write permission or branch protection |
Add permissions: contents: write to the workflow; check branch protection rules |
| Local push to GitHub succeeds but Overleaf never updates | OVERLEAF_TOKEN missing or stale |
Check gh secret list; reset via gh secret set OVERLEAF_TOKEN --body "<new>" |
| Cron runs are skipped / very late | Normal GitHub Actions backlog | Use the sync.ps1 / manual trigger when timing matters |
fatal: Unable to create '.git/index.lock' |
Stale lock from an interrupted git operation (common after WSL/sandbox edits on Windows) | Remove-Item .git/index.lock (PS) or rm .git/index.lock (bash) |
gh pr merge says "not mergeable" right after a force-push |
GitHub's mergeability cache hasn't refreshed | Wait a few seconds and retry |
Architecture notes
Non-obvious details that matter when debugging:
Default branch is
master(Overleaf's convention). The workflows hardcodemaster. Don't rename it.Why this doesn't loop infinitely:
pull-from-overleafpushes to GitHub using the defaultGITHUB_TOKEN. GitHub Actions deliberately does not re-trigger workflow runs on commits made byGITHUB_TOKEN— that's the platform's built-in loop protection. Sopull-from-overleafwriting to master does NOT firesync-overleafback. User pushes from a personal machine DO fire it, because they authenticate with a personal token, notGITHUB_TOKEN.Concurrency group: both workflows share
concurrency: group: sync-overleaf. This serializes them so one doesn't push while the other is mid-fetch. Without this, you can get into states where each workflow sees a stale view of the other side.Two remotes by convention: when cloning from Overleaf and adding GitHub, the natural setup is
origin = Overleaf(from the original clone),github = GitHub(added later). Some setups flip these. The workflows don't care — they always construct the Overleaf URL from scratch using the secret.OVERLEAF_EMAILsecret is vestigial. Earlier auth tried email-as-username; Overleaf migrated to requiring the literalgit. If the secret is set, it's harmless. New setups don't need it.
Token rotation
Overleaf tokens leak. Rotate them periodically and immediately if exposed:
- In Overleaf: Account Settings → Git Integration → revoke the old token, generate a new one
- Update the GitHub secret:
gh secret set OVERLEAF_TOKEN --body "<new-token>" --repo <owner>/<repo> - Manually trigger
pull-from-overleaf.ymlonce to confirm the new token works:gh workflow run pull-from-overleaf.yml --repo <owner>/<repo> - No workflow file changes needed. The next push will also exercise the token via
sync-overleaf.
Papers already set up with this pattern
This skill doubles as a memory for the author's own paper instances. Before treating a paper as "new", check references/instances.md — it lists each paper repo wired up with this pattern, the per-paper config that differs from the defaults, and any pending action items (e.g., tokens scheduled for rotation).
If the user mentions a paper by name and it's already listed there, you already know its Overleaf project ID, GitHub repo, parent-submodule path, and collaboration mode — skip the discovery questions and go straight to the daily-workflow guidance. When a new paper joins the pattern, append a section to instances.md so the next session inherits the context.
What this skill explicitly does NOT do
- It does not build the PDF in CI. If the user wants Actions to compile
paper.pdfon every push, add a separate workflow usingxu-cheng/latex-action@v3. Not in scope here. - It does not support branches other than
master. Overleaf's git endpoint only exposesmaster. If the user wants feature branches, do them on GitHub only and merge to master, which then syncs to Overleaf. - It does not replace Overleaf's paid GitHub integration. The paid integration handles divergence in a UI. This setup is more transparent and free but requires
sync.ps1before sessions and occasional manual conflict resolution. - It does not rebase commits across the boundary. The pull workflow uses fast-forward only, by design. Anything else is a human decision.
Skill contents
SKILL.md— this filereferences/setup-new-paper.md— full procedure for setting up sync on a paper not yet in this patternreferences/conflict-resolution.md— divergence playbookreferences/troubleshooting.md— common errors and fixesreferences/instances.md— author's living log of papers using this pattern + pending action itemsassets/sync-overleaf.yml— push-direction workflow templateassets/pull-from-overleaf.yml— pull-direction workflow templateassets/sync.ps1— PowerShell pre-session helper (Windows)assets/sync.sh— bash pre-session helper (Mac / Linux)