diff options
| author | Elvis Claros Castro <elvis@claros.ar> | 2026-09-26 18:44:59 -0300 |
|---|---|---|
| committer | Elvis Claros Castro <elvis@claros.ar> | 2026-09-26 18:44:59 -0300 |
| commit | 85df8ffe8d6f623e9e195220e5a70edffdad7507 (patch) | |
| tree | 25b9838c5b959be96b532925972a381189f4658a /README.md | |
| download | overleaf-ce-sync-85df8ffe8d6f623e9e195220e5a70edffdad7507.tar.gz overleaf-ce-sync-85df8ffe8d6f623e9e195220e5a70edffdad7507.zip | |
Diffstat (limited to 'README.md')
| -rw-r--r-- | README.md | 99 |
1 files changed, 99 insertions, 0 deletions
diff --git a/README.md b/README.md new file mode 100644 index 0000000..8ac6801 --- /dev/null +++ b/README.md @@ -0,0 +1,99 @@ +# overleaf-ce-sync + +[Español](README.es.md) + +Two-way "git-like" sync for a **self-hosted Overleaf Community Edition**. + +The native Git integration (`git-bridge`) is a Server Pro / paid feature and is +not shippable with CE — the Snapshot API and OAuth2 server it needs live in +closed-source modules. This tool gives you the next best thing: pull a project +to disk, version it with your own `git`, and push changes back. + +It talks to the same HTTP endpoints the web UI uses. The plain-HTTP login that +is broken on overleaf.com (CAPTCHA) works fine on CE because CE has no CAPTCHA. + +## Install + +With [pipx](https://pipx.pypa.io) (isolated venv, `overleaf-ce-sync` on PATH): + +```bash +git clone https://git.all.ar/pub/overleaf-ce-sync.git +cd overleaf-ce-sync +pipx install -e . # -e = editable: source edits apply with no reinstall +``` + +The only dependency is `requests`; Python 3.8 or newer. + +If pipx warns that `~/.local/bin` isn't on PATH, run `pipx ensurepath` and open +a new shell. + +## Usage + +```bash +# 1. log in once per host (cookie saved to ~/.config/overleaf-ce, 0600) +overleaf-ce-sync login --host overleaf.example.com --email you@example.com + +# 2. find your project id +overleaf-ce-sync list + +# 3. clone it into a git repo +overleaf-ce-sync clone <project-id> mybook +cd mybook + +# 4. normal loop +overleaf-ce-sync pull # bring down remote changes (overwrites local) +# ...edit, git commit... +overleaf-ce-sync push # upload locally-changed files +``` + +After the first `pull`/`clone`, the host + project id are remembered in +`.overleaf-ce.json`, so inside the project dir you can just run +`overleaf-ce-sync pull` / `overleaf-ce-sync push`. + +## How it works + +The sync is anchored to **git commits**, so `push` publishes only what you have +committed — never your working-tree scratch edits. + +- **pull** → `GET /Project/<id>/download/zip`, extract over the working tree, + then make a "sync commit" and remember it as `last_sync_commit` in + `.overleaf-ce.json`. +- **push** → upload the **committed** state (HEAD). It diffs + `last_sync_commit..HEAD`, uploads each changed file's *committed* bytes + (`git show HEAD:<path>`) via `POST /Project/<id>/upload` (overwrites by name), + then advances `last_sync_commit` to HEAD. Uncommitted edits are ignored. +- The upload endpoint needs the project's **root folder id**. It's discovered + once over socket.io **xhr-polling** (plain HTTP — works behind any reverse + proxy; no websocket upgrade needed) via the `joinProjectResponse` event, and + cached in `.overleaf-ce.json`. + +## Limitations / notes + +- **push only sends committed changes.** Edit, `git commit`, then `push`. A file + with uncommitted changes is skipped (you'll get a heads-up note). +- **Deletions are not propagated.** Files removed in your commits are reported but + NOT deleted on Overleaf (safer default). Delete them in the web UI; use + `pull --prune` to mirror remote deletions into your working tree. +- **pull overwrites the working tree** and refuses if you have uncommitted + changes (pass `--force` to override). It auto-commits the pulled state, so your + history interleaves "overleaf pull" sync commits with your own. +- **Binary vs doc**: Overleaf decides per file; re-uploading creates new history + versions, so push only sends files whose committed content changed. +- Don't log out in the browser session you used — Overleaf may revoke the cookie. + +## Fallback: root folder id without socket.io + +If the xhr-polling discovery fails against your setup, fetch the id once from +Mongo on the server and pass it with `--root-folder-id` (it gets cached +afterwards): + +```bash +docker compose exec mongo mongosh sharelatex --quiet --eval \ + 'printjson(db.projects.findOne({_id:ObjectId("<project-id>")},{ "rootFolder._id":1}))' + +overleaf-ce-sync push --root-folder-id <root-folder-id> +``` + +## License + +MIT |