Jump to content

Recommended Posts

Posted

Hey folks,

we at frameless Media often develop across multiple devices – laptop, tablet, sometimes even from a phone with an AI coding assistant. Git is our single source of truth, but getting those changes onto a staging or production server has always been annoying. Especially on shared hosting where there's no SSH, no git, and git-based FTP via YAML configs is more hassle than it's worth.

We also frequently need to test new modules directly on shared hosting environments where the server setup differs from our local machines. Manually uploading files after every push? No thanks. So we built GitSync. 🎯

TL;DR:

Link any installed module to its GitHub repo
See all branches and their latest commits
One-click sync – only changed files are downloaded
GitHub Webhook support – auto-sync on every push
Works on shared hosting – no git, no SSH, no cron
Private repo support via GitHub Token

What's the difference to ProcessUpgrade?
ProcessUpgrade is great for updating published modules from the PW modules directory. But it tracks releases, not branches. During development, when you're pushing to `develop` or `feature/xyz` ten times a day, you need something different.

That's where GitSync comes in. 🚀

image.thumb.png.8ec134f43497c064879edf79ef881848.png

How it works

  1. Install the module, add your GitHub Token (optional for public repos)
  2. Go to GitSync > Add Module, pick any installed module from the dropdown
  3. GitSync searches GitHub for matching repositories automatically
  4. Link the module to a repo + branch – done

From now on, you can sync with one click. GitSync compares file hashes locally and remotely (using the same SHA1 blob hashing that git uses internally) and only downloads what actually changed. No full re-downloads, minimal API usage.

Want it fully automatic? Set up a GitHub Webhook – enter a secret in the module config, point the webhook to `https://yoursite.com/gitsync-webhook/`, and every push triggers an automatic sync. The module overview shows a webhook badge on auto-synced modules so you always know what's wired up.

The real power: remote development with AI 📱
You're on the train, phone in hand, chatting with Claude via the Claude app. Claude writes code, commits to a feature branch on GitHub. GitSync picks up the webhook and syncs the module to your dev server. Automatically. You open the edited webpage on your phone, check the result, give feedback, iterate. The entire development loop without ever opening a laptop. 🤯

This works just as well for teams: multiple developers push to GitHub from different machines, and the staging server always reflects the latest state – no manual deploys, no SSH sessions, no FTP. We've been using a prototype internally for a few weeks now and it's become part of our daily workflow – especially the webhook auto-sync is something we don't want to miss anymore. As proof of concept we built the public release entirely as described above 😃

Technical details for the curious
The differential sync works like GIT itself: every file's content is hashed as `sha1("blob {size}\0{content}")`. GitHub's Trees API returns these hashes for the entire branch in a single request. GitSync computes the same hash locally. Matching hash = identical file = skip.

Requirements
ProcessWire >= 3.0 and PHP >= 7.4 with cURL

Module and Docs
👉 GitHub: https://github.com/frameless-at/GitSync
👉 Module Directory: https://processwire.com/modules/git-sync/

Would love to hear your thoughts, ideas, and edge cases we might not have considered!
Cheers, Mike

  • Like 11
  • Thanks 3
  • Mikel changed the title to GitSync – Keep your (private) modules in sync with GitHub
Posted

Update: Improved "Link Module" UX

We had an internal discussion about the "Link Module" interface and optimized how the different states are handled:

Bildschirmfoto2026-04-09um09_26_43.thumb.png.d110b2d7cffd324be364003d86ab47c3.png

Match found – The repo is resolved and ready to link. The green link opens the repository on GitHub so you can verify it's the right one before linking. This appears instantly when the module declares its GitHub URL in getModuleInfo() or has been resolved before (cached), otherwise after a quick GitHub search.

Bildschirmfoto2026-04-09um09_27_13.thumb.png.da768f22507c4e56e633a50f6ec3da24.png

No repo found – ProModules like RepeaterMatrix have no public GitHub repo. GitSync shows a clear "No repositories found." instead of false matches.

Bildschirmfoto2026-04-09um09_27_35.thumb.png.92ca74a69a2653851888811351e49df3.png

Multiple repos – When a module exists in several repos (forks, different maintainers), you get a list to pick from.

Bildschirmfoto2026-04-09um09_27_48.thumb.png.d63be8f3b39aa14144833c549f06e159.png

Selected, with "change" – After picking one, a "change" link lets you switch. It only appears when there are actually alternatives.

Other improvements:

  • single results are now auto-selected (no unnecessary click),
  • the GitHub search uses Code Search API for exact .module.php filename matching (works even when repo name ≠ class name),
  • and results are cached client-side so re-selecting a module is instant.

Cheers, Mike

  • 1 month later...
Posted

Update – Auto-Sync for third party modules – v0.2.0
  
Hi, folks, a new feature for the GitSync module: for public third-party modules where you can't add a webhook on the source repo, GitSync can now check for upstream updates automatically.

image.thumb.png.583c62366db42249ce2d343d1c07dd6c.png

How it works
Every linked module has a new per-row "Auto-Sync" setting in the GitSync overview:

  • off – manual sync only (default)
  • notify – on the first admin page load per session, GitSync queries GitHub for the tracked branch. If a newer commit exists, a warning notice appears with a direct link to the branches view:
    image.png.1d83bf1b8880ad66541a5e9c974ae5f2.png
     
  • auto-sync – same check, but performs the sync immediately without confirmation.

Once a remote update has been detected, an orange "update available" badge stays on the GitSync overview next to the affected module – so the info is still there after dismissing the notice.

Throttling and scope

  • Checks run once per session (right after admin login), not on every page load.
  • Webhook-active mappings are skipped entirely.
  • GitSync itself is excluded from auto-sync – self-updates remain manual.

Why we built it
Webhooks are the cleanest path for repos you own. For public modules from other authors you don't control, you'd previously have to remember to check for updates manually. Now the module nudges you on login (or syncs straight away if you trust the upstream).

Feedback welcome,
Cheers, Mike

  • Thanks 1
Posted

@mikel, would you mind fixing the issue I reported? How your module compare to ProcessWireUpgrade module form Ryan? Just to let you know, I couldn't upgrade TracyDebugger. I guess to many files? I also successfuly upgraded NativeAnalytics, but the module didn't worked well, like something was missing, maybe incomplete download or something? Not sure how to debug...

  • Like 1
Posted
1 hour ago, matjazp said:

@mikel, would you mind fixing the issue I reported? 

Hi, @matjazp thanks for the report! We fixed it by removing the 3 calls. Just upgrade the module to 0.2.1
 

1 hour ago, matjazp said:

How your module compare to ProcessWireUpgrade module form Ryan? 

They solve different problems. ProcessWireUpgrade is for stable releases, GitSync is for branch-based development workflows.

ProcessWireUpgrade pulls from the official modules.processwire.com directory and compares semantic version numbers. It can upgrade the ProcessWire core itself (master or dev branch) and existing installed modules, but it cannot install new modules that aren't already present, doesn't support private repositories, doesn't support arbitrary branches per module, and uses a pull model (no webhook / no auto-sync on push). Each upgrade is a full download.

GitSync pulls from any GitHub repository — public or private (using a fine-grained Personal Access Token for the latter). It works at the branch and commit level rather than the release level, lets you switch any linked module to any branch, and detects changes by comparing git blob SHAs file-by-file, so only modified files are downloaded. It can install brand-new modules from a GitHub URL (even ones not listed in the official directory), supports private repos, and offers GitHub webhook integration for automatic sync on every push. It does not upgrade the ProcessWire core.

When to use ProcessWireUpgrade:

  • Production servers that should only move to officially released versions
  • Upgrading the ProcessWire core itself
  • Mostly relying on modules from the official directory

When to use GitSync:

  • Test/staging servers that should track a development branch (e.g. develop, feature-x) live
  • Deploying your own modules from private repositories without FTP
  • Installing GitHub-hosted modules that aren't (yet) in the official directory
  • Auto-deploy on every git push via webhook

They can be combined: use ProcessWireUpgrade for the core, and GitSync for modules that you develop yourself or that aren´t in the PW directory.
 

1 hour ago, matjazp said:

Just to let you know, I couldn't upgrade TracyDebugger. I guess to many files?

To narrow this down — could you share:

  1. Which action failed? Install from GitHub, Link Module, or Sync/Upgrade of an already-linked TracyDebugger?
  2. The error or behavior you saw — blank page, timeout, rate-limit message, partial sync, etc.
  3. The last lines of the gitsync log under Setup > Logs > gitsync.
  4. Whether you have a GitHub Personal Access Token configured (without one you're capped at 60 API requests/hour).

The file count alone (~1,250) shouldn't be a problem for a normal upgrade — only changed files are downloaded. But it would be  a problem for a fresh Install from GitHub, where every file is fetched in its own API call. The log will tell us which case you hit.

image.thumb.png.f4317dd1dc0fe7ee5dd7d26855c3e506.png

We just tested and ran into zero problems:

  • We installed Tracy via the Modules page
  • added it to GitSync via the dropdown
  • synced master branch

One known gotcha worth checking: TracyDebugger writes runtime files (logs, bluescreens, dumps) into its own module directory. GitSync deletes local files that don't exist in the remote repo, so a large toDelete list with permission-protected files could also cause the upgrade to fail mid-way.

Cheers,
Mike

  • Like 2
Posted

I think if you connect repositories to Codex Cloud, then configure GitSync on the server, it should work in auto-deployment mode. 
But I haven't checked.

image.thumb.png.2337ba49ab45aa9633fde4a691016adb.png

@Mikel you are try this chain?

Posted

@Mikel, thanks for the clarification. Just for info, I've forked the ProcessWireUpgrade module and modified it so it can upgrade modules (and dev core) to the latest commit, even if there is no version bump.

For the issues I have: the main issue is that the request timeout and I get http error 500 making the download incomplete. I have GH api key.

2026-05-13 05:12:16    admin   http://localhost/processwire/setup/gitsync/sync/  [manual] Sync "TracyDebugger" branch "master": 1121 remote, 4437 local, 1115 to update, 4430 to delete
2026-05-13 05:12:17    admin   http://localhost/processwire/setup/gitsync/sync/    Updated: .gitattributes (48 bytes)
...
2026-05-13 05:13:55    admin   http://localhost/processwire/setup/gitsync/sync/    Updated: scripts/ace-editor/mode-batchfile.js (5187 bytes)

All together it managed to fetch 268 files in ~100 seconds.

Posted
7 hours ago, maximus said:

I think if you connect repositories to Codex Cloud, then configure GitSync on the server, it should work in auto-deployment mode. 
But I haven't checked.

Hi, @maximus, since we do not use OpenAI products (company policy) I haven´t tried Codex at all. But nevertheless, that workflow works — GitSync reacts to any git push, regardless of whether the commit comes from a human, CI, or an agent like Codex Cloud.

@matjazpThanks for the detailed log — that pinpointed the problem clearly. Three fixes are now in main:

  1. PHP execution timeout lifted during sync (set_time_limit(0)). The 500 you hit was almost certainly PHP killing the script at 30–60 s, not GitHub timing out.
  2. Large diffs now use the ZIP archive endpoint. When more than 50 files need updating, GitSync fetches a single ZIP of the branch via /zipball/{ref} and copies from the extracted archive instead of hitting /git/blobs/{sha} once per file. Your 1115-file diff turns from ~1115 sequential HTTPS requests into 1 download plus local copies.
  3. GitSync now respects two sources at once: .gitignore (which spares Tracy's runtime files like logs/, dumps/, bluescreen/) and .gitattributes export-ignore (which keeps maintainer-excluded files like docs/ and .gitattributes itself off the live  install). Both apply bidirectionally — files matching either are never added by the sync and never deleted from local. End result for TracyDebugger: install + first sync = "up to date" with zero unnecessary churn.

Please switch to the GitSync branch refactor-install-from-Github in GitSync and give it another try — the log line for the sync should now show N to update, M to delete (P preserved via .gitignore/.gitattributes) – both N and M considerably lower than your original 1115/4430, and you'll also see Using ZIP archive for N file update(s) whenever the update count is above the 50-file threshold.

We will merge the branch into main after some internal testing, so feedback is welcome 😉
Cheers, Mike

Posted

Thanks for the fast response. Unfortunatelly the sync screw up the TracyDebugger (Error: Failed opening required 'C:\inetpub\wwwroot\site\modules\TracyDebugger/tracy-2.12.x/src/tracy.php') and I had to restore it form the GH repo. In the logs: 

2026-05-13 09:28:15    admin   http://localhost/processwire/setup/gitsync/sync/  [manual] Sync "TracyDebugger" branch "master": 1121 remote, 987 local, 980 to update, 980 to delete (134 preserved via .gitignore/.gitattributes)
2026-05-13 09:28:23    admin   http://localhost/processwire/setup/gitsync/sync/    Using ZIP archive for 980 file update(s) (>50 threshold)
... 980 files updated ...
2026-05-13 09:28:27    admin    http://localhost/processwire/setup/gitsync/sync/    [manual] Synced "TracyDebugger" to branch "master" (commit 068bbd9) – 980 updated, 980 deleted

Not sure what happened, but when the site failed with the exception, the TracyDebugger folder only had 6 files on the root. Other folders, like assets, panels, includes were all missing. It's odd since the log shows:

2026-05-10 18:25:51    admin   http://localhost/processwire/setup/gitsync/sync/    Updated: panels/AdminToolsPanel.php (8396 bytes)
...
2026-05-10 18:25:47    admin    http://localhost/processwire/setup/gitsync/sync/      Updated: includes/post_processors/AdminToolsPost.php (4138 bytes)

Will try to debug later, I'm at work now 🙂

Posted

Sorry, @matjazp, that's a genuine data-loss bug on Windows, on us.

Root cause: buildLocalFileMap was using SplFileInfo::getPathname() which returns backslash paths on Windows, while the remote tree uses forward slashes. So every subdirectory file looked simultaneously "new" (not in local) AND "deleted" (not in remote) because the key comparison failed. The ZIP path then wrote 980 files and the delete pass immediately removed the same 980 files via their backslash variant — which on NTFS resolves to
the same file. That's exactly the "6 files left in root" pattern you saw.

Fix is now in branch refactor-install-from-Github: relative paths are normalized to forward slashes in buildLocalFileMap regardless of OS.

A few things to double-check before you retry:

  1. Sync GitSync branch "refactor-install-from-Github" to latest commit 0f1425e
  2. Make sure your manually-restored TracyDebugger matches the upstream master (which it should if you grabbed it from the GH repo directly)
  3. Run sync — log should show something like 0 to update, 0 to delete (134 preserved via .gitignore/.gitattributes) and "up to date" 

 If your restore was from git clone rather than the GitHub ZIP, your local will have .gitattributes and docs/ present — those are now matched by the .gitattributes export-ignore filter, so they'll be preserved rather than deleted.

Thanks for the precise log output — without the file-count math the Windows-only nature wouldn't have been obvious. Linux/macOS users were unaffected because SplFileInfo::getPathname() already returns forward slashes there.

Cheers, Mike

Posted

[manual] Sync "TracyDebugger" branch "master": 1121 remote, 987 local, 0 to update, 0 to delete (134 preserved via .gitignore/.gitattributes)

Yep. Thanks for quick fix!

Create an account or sign in to comment

You need to be a member in order to leave a comment

Create an account

Sign up for a new account in our community. It's easy!

Register a new account

Sign in

Already have an account? Sign in here.

Sign In Now
  • Recently Browsing   0 members

    • No registered users viewing this page.
×
×
  • Create New...