Add cross-machine sync tooling (clone/pull/push all module repos)
repos.txt + sync-*.sh let any machine (Mac or Windows/Git Bash) hydrate all 49 module repos and keep them in sync via GitHub+gitea. Pulls are fast-forward only (never clobber local work); pushes send commits only and flag uncommitted repos. See SYNC.md for the two-machine workflow. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
This commit is contained in:
58
SYNC.md
Normal file
58
SYNC.md
Normal file
@@ -0,0 +1,58 @@
|
|||||||
|
# Syncing Odoo-Modules across machines (Mac + Windows)
|
||||||
|
|
||||||
|
Each module/suite folder here is its **own git repo** (private on GitHub at
|
||||||
|
`gsinghpal/<name>`, mirrored to gitea `admin/<name>`). This parent folder is a
|
||||||
|
separate repo that holds the shared files (CLAUDE.md, docs, scripts, these sync
|
||||||
|
helpers). The cloud (GitHub) is the hub: both machines push to it and pull from it.
|
||||||
|
|
||||||
|
Nothing here ever deletes your work. Pulls are fast-forward only, so local changes
|
||||||
|
are never overwritten; pushes only send commits.
|
||||||
|
|
||||||
|
## First-time setup on a new machine (e.g. the Windows PC)
|
||||||
|
|
||||||
|
1. Install **Git** (Git for Windows includes "Git Bash", which runs these scripts).
|
||||||
|
2. Sign in to GitHub once so git can push/pull:
|
||||||
|
- easiest: `gh auth login` (or let Git Credential Manager prompt on first pull)
|
||||||
|
3. Get everything:
|
||||||
|
```
|
||||||
|
git clone https://github.com/gsinghpal/Odoo-Modules.git
|
||||||
|
cd Odoo-Modules
|
||||||
|
bash sync-clone-all.sh
|
||||||
|
```
|
||||||
|
That clones the parent, then all 49 module repos into place.
|
||||||
|
|
||||||
|
(gitea is an optional second mirror. The first push to it will ask for your
|
||||||
|
`git.nexasystems.ca` login. If you only use GitHub, those gitea lines just fail
|
||||||
|
quietly and GitHub stays the source of truth.)
|
||||||
|
|
||||||
|
## Daily workflow (same on Mac and Windows)
|
||||||
|
|
||||||
|
- **Before you start:** `bash sync-pull-all.sh` - pulls the latest for the parent
|
||||||
|
and every module. Anything with local changes or a diverged history is skipped and
|
||||||
|
listed, so you can handle it yourself.
|
||||||
|
- **Do your work**, then **commit inside the module(s) you changed**:
|
||||||
|
```
|
||||||
|
cd fusion_clock
|
||||||
|
git add -A
|
||||||
|
git commit -m "..."
|
||||||
|
cd ..
|
||||||
|
```
|
||||||
|
- **When done:** `bash sync-push-all.sh` - pushes every committed change to GitHub
|
||||||
|
+ gitea, and flags any repo that still has uncommitted changes (so nothing is
|
||||||
|
silently left behind).
|
||||||
|
|
||||||
|
## Golden rule for two machines
|
||||||
|
|
||||||
|
Push from the machine you worked on **before** you switch to the other one, and run
|
||||||
|
`sync-pull-all.sh` on the other machine **before** you start. That keeps both in sync
|
||||||
|
and avoids diverged histories.
|
||||||
|
|
||||||
|
## Helper scripts
|
||||||
|
|
||||||
|
| Script | What it does |
|
||||||
|
|--------|--------------|
|
||||||
|
| `sync-clone-all.sh` | Clone any module repo listed in `repos.txt` that isn't here yet. |
|
||||||
|
| `sync-pull-all.sh` | Fast-forward pull the parent + all modules (safe, never clobbers). |
|
||||||
|
| `sync-push-all.sh` | Push committed work for the parent + all modules to GitHub + gitea. |
|
||||||
|
| `sync-refresh-list.sh` | Rebuild `repos.txt` from the repos present here (after adding/removing a module). |
|
||||||
|
| `repos.txt` | The list of module repo names the scripts act on. |
|
||||||
52
repos.txt
Normal file
52
repos.txt
Normal file
@@ -0,0 +1,52 @@
|
|||||||
|
# Module repos that live as independent git repos inside this folder.
|
||||||
|
# Owner: github.com/gsinghpal/<name> + gitea git.nexasystems.ca/admin/<name>
|
||||||
|
# One repo name per line. Used by sync-*.sh. Regenerate with: bash sync-refresh-list.sh
|
||||||
|
disable_iap_calls
|
||||||
|
disable_odoo_online
|
||||||
|
disable_publisher_warranty
|
||||||
|
fusion_accounts
|
||||||
|
fusion_api
|
||||||
|
fusion_canada_post
|
||||||
|
fusion_centralize_billing
|
||||||
|
fusion_chatter_enhance
|
||||||
|
fusion_claims
|
||||||
|
fusion_clock
|
||||||
|
fusion_clock_ai
|
||||||
|
fusion_clover
|
||||||
|
fusion_digitize
|
||||||
|
fusion_faxes
|
||||||
|
fusion_helpdesk
|
||||||
|
fusion_helpdesk_central
|
||||||
|
fusion_inventory
|
||||||
|
fusion_loaners_management
|
||||||
|
fusion_login_audit
|
||||||
|
fusion_ltc_management
|
||||||
|
fusion_notes
|
||||||
|
fusion_odoo_fixes
|
||||||
|
fusion_payroll
|
||||||
|
fusion_pdf_preview
|
||||||
|
fusion_planning
|
||||||
|
fusion_portal
|
||||||
|
fusion_poynt
|
||||||
|
fusion_rental
|
||||||
|
fusion_repairs
|
||||||
|
fusion_reports_templates
|
||||||
|
fusion_ringcentral
|
||||||
|
fusion_schedule
|
||||||
|
fusion_service_charges
|
||||||
|
fusion_shipping
|
||||||
|
fusion_so_to_po
|
||||||
|
fusion_tasks
|
||||||
|
fusion_templates
|
||||||
|
fusion_theme_switcher
|
||||||
|
fusion_voip_ringcentral
|
||||||
|
fusion_whitelabels
|
||||||
|
network_logger
|
||||||
|
nexa_coa_setup
|
||||||
|
fusion_plating
|
||||||
|
fusion_accounting
|
||||||
|
fusion_iot
|
||||||
|
fusion_labels
|
||||||
|
fusion_projects
|
||||||
|
fusion-statements
|
||||||
|
fusion-woo-odoo
|
||||||
28
sync-clone-all.sh
Executable file
28
sync-clone-all.sh
Executable file
@@ -0,0 +1,28 @@
|
|||||||
|
#!/usr/bin/env bash
|
||||||
|
# Clone every module repo listed in repos.txt into this folder.
|
||||||
|
# Run once on a new machine, right after you clone the parent Odoo-Modules repo.
|
||||||
|
# Adds both GitHub (origin) and gitea remotes. Never deletes or overwrites folders.
|
||||||
|
#
|
||||||
|
# Mac/Linux: bash sync-clone-all.sh
|
||||||
|
# Windows : open "Git Bash" here, then: bash sync-clone-all.sh
|
||||||
|
set -uo pipefail
|
||||||
|
cd "$(dirname "$0")" || exit 1
|
||||||
|
GH="https://github.com/gsinghpal"
|
||||||
|
GITEA="https://git.nexasystems.ca/admin"
|
||||||
|
[ -f repos.txt ] || { echo "repos.txt not found next to this script"; exit 1; }
|
||||||
|
|
||||||
|
cloned=0; skipped=0; failed=0
|
||||||
|
while IFS= read -r f; do
|
||||||
|
case "$f" in ''|\#*) continue ;; esac
|
||||||
|
if [ -d "$f/.git" ]; then
|
||||||
|
echo "skip (already here): $f"; skipped=$((skipped+1)); continue
|
||||||
|
fi
|
||||||
|
if git clone -q "$GH/$f.git" "$f"; then
|
||||||
|
git -C "$f" remote add gitea "$GITEA/$f.git" 2>/dev/null || true
|
||||||
|
echo "cloned: $f"; cloned=$((cloned+1))
|
||||||
|
else
|
||||||
|
echo "FAILED: $f"; failed=$((failed+1))
|
||||||
|
fi
|
||||||
|
done < repos.txt
|
||||||
|
echo "----------------------------------------"
|
||||||
|
echo "cloned=$cloned skipped=$skipped failed=$failed"
|
||||||
44
sync-pull-all.sh
Executable file
44
sync-pull-all.sh
Executable file
@@ -0,0 +1,44 @@
|
|||||||
|
#!/usr/bin/env bash
|
||||||
|
# Pull the latest for the parent repo + every module repo.
|
||||||
|
# Fast-forward only: it NEVER overwrites or deletes your local work. If a repo has
|
||||||
|
# diverged or has conflicting local changes, it is skipped and reported so you can
|
||||||
|
# handle it by hand. Clones any module folder that is missing.
|
||||||
|
# Run this before you start working on a machine.
|
||||||
|
#
|
||||||
|
# bash sync-pull-all.sh
|
||||||
|
set -uo pipefail
|
||||||
|
cd "$(dirname "$0")" || exit 1
|
||||||
|
GH="https://github.com/gsinghpal"
|
||||||
|
GITEA="https://git.nexasystems.ca/admin"
|
||||||
|
|
||||||
|
pull_one() {
|
||||||
|
local d="$1"
|
||||||
|
local br; br=$(git -C "$d" symbolic-ref --short HEAD 2>/dev/null || echo main)
|
||||||
|
if git -C "$d" pull --ff-only -q origin "$br" 2>/dev/null; then
|
||||||
|
echo "updated: $d"
|
||||||
|
else
|
||||||
|
if [ -n "$(git -C "$d" status --porcelain --untracked-files=no)" ]; then
|
||||||
|
echo "SKIP (uncommitted changes block ff): $d -> commit, then re-run"
|
||||||
|
else
|
||||||
|
echo "SKIP (diverged, needs manual merge): $d"
|
||||||
|
fi
|
||||||
|
fi
|
||||||
|
}
|
||||||
|
|
||||||
|
echo "== parent =="
|
||||||
|
pull_one .
|
||||||
|
[ -f repos.txt ] || { echo "repos.txt not found"; exit 1; }
|
||||||
|
echo "== modules =="
|
||||||
|
while IFS= read -r f; do
|
||||||
|
case "$f" in ''|\#*) continue ;; esac
|
||||||
|
if [ ! -d "$f/.git" ]; then
|
||||||
|
if git clone -q "$GH/$f.git" "$f"; then
|
||||||
|
git -C "$f" remote add gitea "$GITEA/$f.git" 2>/dev/null || true
|
||||||
|
echo "cloned (was missing): $f"
|
||||||
|
else
|
||||||
|
echo "FAILED clone: $f"
|
||||||
|
fi
|
||||||
|
continue
|
||||||
|
fi
|
||||||
|
pull_one "$f"
|
||||||
|
done < repos.txt
|
||||||
36
sync-push-all.sh
Executable file
36
sync-push-all.sh
Executable file
@@ -0,0 +1,36 @@
|
|||||||
|
#!/usr/bin/env bash
|
||||||
|
# Push your committed work for the parent + every module repo to BOTH GitHub
|
||||||
|
# (origin) and gitea. Only commits are pushed. Any repo that still has UNCOMMITTED
|
||||||
|
# changes is flagged, so you can see exactly what has not been synced yet.
|
||||||
|
# Run this after you finish working, once you have committed your changes.
|
||||||
|
#
|
||||||
|
# bash sync-push-all.sh
|
||||||
|
set -uo pipefail
|
||||||
|
cd "$(dirname "$0")" || exit 1
|
||||||
|
|
||||||
|
push_one() {
|
||||||
|
local d="$1"
|
||||||
|
local br; br=$(git -C "$d" symbolic-ref --short HEAD 2>/dev/null || echo main)
|
||||||
|
local flag=""
|
||||||
|
[ -n "$(git -C "$d" status --porcelain --untracked-files=no)" ] && flag=" [HAS UNCOMMITTED CHANGES - not pushed]"
|
||||||
|
local any=0
|
||||||
|
for r in origin gitea; do
|
||||||
|
git -C "$d" remote get-url "$r" >/dev/null 2>&1 || continue
|
||||||
|
any=1
|
||||||
|
if git -C "$d" push -q "$r" "$br" 2>/dev/null; then
|
||||||
|
echo "pushed: $d -> $r$flag"
|
||||||
|
else
|
||||||
|
echo "push FAILED (or nothing to push): $d -> $r$flag"
|
||||||
|
fi
|
||||||
|
done
|
||||||
|
[ "$any" = 0 ] && echo "no remotes: $d"
|
||||||
|
}
|
||||||
|
|
||||||
|
echo "== parent =="
|
||||||
|
push_one .
|
||||||
|
[ -f repos.txt ] || { echo "repos.txt not found"; exit 1; }
|
||||||
|
echo "== modules =="
|
||||||
|
while IFS= read -r f; do
|
||||||
|
case "$f" in ''|\#*) continue ;; esac
|
||||||
|
if [ -d "$f/.git" ]; then push_one "$f"; else echo "skip (not cloned): $f"; fi
|
||||||
|
done < repos.txt
|
||||||
23
sync-refresh-list.sh
Executable file
23
sync-refresh-list.sh
Executable file
@@ -0,0 +1,23 @@
|
|||||||
|
#!/usr/bin/env bash
|
||||||
|
# Regenerate repos.txt from the actual module folders present here (any top-level
|
||||||
|
# folder that is its own git repo with a github.com/gsinghpal origin). Run this
|
||||||
|
# after you add or remove a module repo, then commit repos.txt so both machines
|
||||||
|
# pick up the change.
|
||||||
|
#
|
||||||
|
# bash sync-refresh-list.sh
|
||||||
|
set -uo pipefail
|
||||||
|
cd "$(dirname "$0")" || exit 1
|
||||||
|
|
||||||
|
{
|
||||||
|
echo "# Module repos that live as independent git repos inside this folder."
|
||||||
|
echo "# Owner: github.com/gsinghpal/<name> + gitea git.nexasystems.ca/admin/<name>"
|
||||||
|
echo "# One repo name per line. Used by sync-*.sh. Regenerate with: bash sync-refresh-list.sh"
|
||||||
|
for d in */; do
|
||||||
|
d="${d%/}"
|
||||||
|
[ -d "$d/.git" ] || continue
|
||||||
|
url=$(git -C "$d" remote get-url origin 2>/dev/null || true)
|
||||||
|
case "$url" in *github.com/gsinghpal/*) echo "$d" ;; esac
|
||||||
|
done
|
||||||
|
} > repos.txt.tmp && mv repos.txt.tmp repos.txt
|
||||||
|
|
||||||
|
echo "repos.txt now lists $(grep -cvE '^\s*#|^\s*$' repos.txt) repos"
|
||||||
Reference in New Issue
Block a user