aboutsummaryrefslogtreecommitdiffstats
path: root/README.md
diff options
context:
space:
mode:
authorElvis Claros Castro <elvis@claros.ar>2026-09-26 18:44:59 -0300
committerElvis Claros Castro <elvis@claros.ar>2026-09-26 18:44:59 -0300
commit85df8ffe8d6f623e9e195220e5a70edffdad7507 (patch)
tree25b9838c5b959be96b532925972a381189f4658a /README.md
downloadoverleaf-ce-sync-main.tar.gz
overleaf-ce-sync-main.zip
Initial import: two-way sync CLI for self-hosted Overleaf CEHEADmain
Diffstat (limited to 'README.md')
-rw-r--r--README.md99
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