chore(mirror): keep the Gitea fallback current from local git (#38)
GitHub is the source of truth; Gitea (origin) is a just-in-case copy that had drifted two commits behind. Rather than require a credential on GitHub's side or convert the repo to a read-only Gitea mirror, wire it into the local workflow: - scripts/mirror-gitea.sh — pushes prod + all tags to the Gitea remote (--branches for every branch). Refuses to push to a GitHub remote, prints what it did, exits non-zero when the mirror could not be updated. - scripts/hooks/pre-push — git has no post-push hook, so the mirror runs before a push that includes prod/main or a tag; skips other remotes, deletions and its own recursion. - scripts/hooks/post-merge — refreshes the mirror after `git pull`/merge on prod, which is how a PR squash-merged on GitHub reaches the mirror. - scripts/install-git-hooks.sh — one-time per clone; sets core.hooksPath=scripts/hooks so the hooks stay versioned with the repo. Mirror failures are warnings: a Gitea outage never blocks a push to GitHub. AGENTS.md documents the setup plus the GitHub-only release flow. Verified: script + hooks exercised for the prod/tag case, the feature-branch skip, the Gitea-remote skip and the recursion guard; the mirror is idempotent (no-op when already in sync).
This commit is contained in:
@@ -104,7 +104,7 @@ gridline/
|
||||
├── .github/workflows/ # release.yml (monorepo paths) + auto-assign.yml
|
||||
├── docs/ # Project docs
|
||||
├── screenshots/ # README marketing images
|
||||
├── scripts/ # Release tooling (render-cask.sh)
|
||||
├── scripts/ # Release tooling (render-cask.sh, release-mac.sh) + Gitea mirror (mirror-gitea.sh, hooks/, install-git-hooks.sh)
|
||||
└── AGENTS.md # This file
|
||||
```
|
||||
|
||||
@@ -177,9 +177,16 @@ cargo test # Rust tests (from desktop/sr
|
||||
|
||||
Direct pushes to `prod` will be bypassed only in emergencies; prefer the PR path so changes land through the PR gate.
|
||||
|
||||
**The Gitea fallback keeps itself current — run `scripts/install-git-hooks.sh` once per clone.** It sets `core.hooksPath=scripts/hooks`, so:
|
||||
|
||||
- `pre-push` mirrors `prod` + all tags to `origin` (Gitea) whenever you push to the `github` remote,
|
||||
- `post-merge` mirrors again after a `git pull`/merge while on `prod` (a PR squash-merged on GitHub only reaches the mirror when the local `prod` pulls it).
|
||||
|
||||
`scripts/mirror-gitea.sh` does the same by hand (`--branches` also pushes feature branches); it refuses to push to a GitHub remote and exits non-zero if the mirror could not be updated. The hooks downgrade that to a warning — **a Gitea outage never blocks a push to GitHub**.
|
||||
|
||||
### Releases
|
||||
|
||||
**Releases are GitHub-only.** Tag `prod` and push the tag to the `github` remote — `git tag vN.M.N && git push github vN.M.N`. GitHub Actions (`release.yml`) builds installers for macOS (Apple Silicon + Intel), Windows, and Linux and opens a **draft** release (review + publish on GitHub). Never push release tags to `origin`/Gitea; it exists only as a fallback copy of the code.
|
||||
**Releases are GitHub-only.** Tag `prod` and push the tag to the `github` remote — `git tag vN.M.N && git push github vN.M.N`. GitHub Actions (`release.yml`) builds installers for macOS (Apple Silicon + Intel), Windows, and Linux and opens a **draft** release (review + publish on GitHub). The Gitea tag arrives automatically via the `pre-push` hook (or `scripts/mirror-gitea.sh`) — never `git push origin vN.M.N` expecting a build, and never publish a release on Gitea.
|
||||
|
||||
**Before tagging**, keep everything in sync:
|
||||
- Version number across `desktop/package.json`, `desktop/src-tauri/Cargo.toml`, `desktop/src-tauri/tauri.conf.json`, and `www/src/pages/index.astro` (the landing page version string)
|
||||
|
||||
Executable
+25
@@ -0,0 +1,25 @@
|
||||
#!/bin/sh
|
||||
#
|
||||
# post-merge hook — after `git pull`/merge, refresh the Gitea fallback.
|
||||
#
|
||||
# Installed via `scripts/install-git-hooks.sh` (git core.hooksPath = scripts/hooks).
|
||||
# GitHub merges (e.g. a squash-merged PR) happen server-side, so a local
|
||||
# `git pull` on prod is the moment to bring the Gitea mirror along.
|
||||
#
|
||||
# args: $1 = 1 when the merge was a squash, otherwise 0
|
||||
|
||||
if [ "${GRIDLINE_MIRROR_RUNNING:-}" = "1" ]; then
|
||||
exit 0
|
||||
fi
|
||||
|
||||
branch="$(git symbolic-ref --short -q HEAD)" || exit 0
|
||||
case "$branch" in
|
||||
prod|main) ;;
|
||||
*) exit 0 ;;
|
||||
esac
|
||||
|
||||
root="$(git rev-parse --show-toplevel)" || exit 0
|
||||
GRIDLINE_MIRROR_RUNNING=1 sh "$root/scripts/mirror-gitea.sh" ||
|
||||
echo "post-merge: warning: Gitea mirror was not updated" >&2
|
||||
|
||||
exit 0
|
||||
Executable
+46
@@ -0,0 +1,46 @@
|
||||
#!/bin/sh
|
||||
#
|
||||
# pre-push hook — mirrors the trunk branch and tags to the Gitea fallback.
|
||||
#
|
||||
# Installed via `scripts/install-git-hooks.sh` (git core.hooksPath = scripts/hooks).
|
||||
# Git has no client-side post-push hook, so the mirror runs here: the refs are
|
||||
# already committed locally, so whether Gitea or GitHub receives them first makes
|
||||
# no difference. A Gitea outage prints a warning and never blocks the push.
|
||||
#
|
||||
# stdin receives one line per ref being pushed:
|
||||
# <local-ref> <local-sha> <remote-ref> <remote-sha>
|
||||
#
|
||||
# args: $1 = remote name, $2 = remote URL
|
||||
|
||||
remote_url="${2:-}"
|
||||
|
||||
# Only react to pushes of the canonical GitHub remote.
|
||||
case "$remote_url" in
|
||||
*github.com*) ;;
|
||||
*) exit 0 ;;
|
||||
esac
|
||||
|
||||
# Don't recurse when the mirror script itself is pushing to Gitea.
|
||||
if [ "${GRIDLINE_MIRROR_RUNNING:-}" = "1" ]; then
|
||||
exit 0
|
||||
fi
|
||||
|
||||
mirror_needed=0
|
||||
while read -r _local_ref local_sha remote_ref _remote_sha; do
|
||||
# Ignore deletions (all-zero object id).
|
||||
case "$local_sha" in
|
||||
0000000000000000000000000000000000000000) continue ;;
|
||||
esac
|
||||
case "$remote_ref" in
|
||||
refs/heads/prod|refs/heads/main|refs/tags/*) mirror_needed=1 ;;
|
||||
esac
|
||||
done
|
||||
|
||||
[ "$mirror_needed" = "1" ] || exit 0
|
||||
|
||||
root="$(git rev-parse --show-toplevel)" || exit 0
|
||||
GRIDLINE_MIRROR_RUNNING=1 sh "$root/scripts/mirror-gitea.sh" ||
|
||||
echo "pre-push: warning: Gitea mirror was not updated (continuing with the push)" >&2
|
||||
|
||||
# Never block the push to GitHub because of a mirror problem.
|
||||
exit 0
|
||||
Executable
+37
@@ -0,0 +1,37 @@
|
||||
#!/usr/bin/env bash
|
||||
#
|
||||
# Install the repo's git hooks (once per clone).
|
||||
#
|
||||
# Points `core.hooksPath` at `scripts/hooks/` so the hook files are versioned
|
||||
# with the repo instead of copied into `.git/hooks` (a stale copy can't drift
|
||||
# out of date this way). Currently:
|
||||
#
|
||||
# pre-push — mirror prod + tags to the Gitea fallback (git.ranio.xyz)
|
||||
# post-merge — same, after a `git pull`/merge while on prod
|
||||
#
|
||||
# The mirror itself lives in `scripts/mirror-gitea.sh`; run it by hand any time
|
||||
# with `scripts/mirror-gitea.sh`. Gitea is a just-in-case copy: GitHub is the
|
||||
# source of truth, and a Gitea outage never blocks a push.
|
||||
|
||||
set -euo pipefail
|
||||
|
||||
repo_root="$(git rev-parse --show-toplevel 2>/dev/null)" || {
|
||||
echo "install-git-hooks: not inside a git repository" >&2
|
||||
exit 1
|
||||
}
|
||||
cd "$repo_root"
|
||||
|
||||
if [ ! -d scripts/hooks ]; then
|
||||
echo "install-git-hooks: scripts/hooks/ is missing — run this from the repo root" >&2
|
||||
exit 1
|
||||
fi
|
||||
|
||||
chmod +x scripts/hooks/* scripts/mirror-gitea.sh 2>/dev/null || true
|
||||
git config core.hooksPath scripts/hooks
|
||||
|
||||
gitea_remote="${GRIDLINE_GITEA_REMOTE:-origin}"
|
||||
echo "install-git-hooks: core.hooksPath -> scripts/hooks"
|
||||
for hook in scripts/hooks/*; do
|
||||
echo "install-git-hooks: $(basename "$hook")"
|
||||
done
|
||||
echo "install-git-hooks: prod + tags mirror to the '$gitea_remote' remote on push/pull"
|
||||
Executable
+90
@@ -0,0 +1,90 @@
|
||||
#!/usr/bin/env bash
|
||||
#
|
||||
# Keep the Gitea "just in case" mirror (git.ranio.xyz) in step with GitHub.
|
||||
#
|
||||
# GitHub (`AdrianBonpin/gridline`, remote `github`) is the source of truth; the
|
||||
# Gitea remote (`origin`) exists only as a fallback copy of the code, so this
|
||||
# mirrors the trunk branch and every tag — not feature branches.
|
||||
#
|
||||
# Run it manually after a merge that happened on GitHub, or let the repo's git
|
||||
# hooks call it (`scripts/install-git-hooks.sh` wires up `core.hooksPath`):
|
||||
# * pre-push — before pushing prod/tags to GitHub
|
||||
# * post-merge — after `git pull`/merge while on prod
|
||||
#
|
||||
# Exit status is non-zero when the mirror could not be updated, so callers can
|
||||
# decide whether that is fatal. The hooks treat it as a warning: a Gitea outage
|
||||
# must never block a push to GitHub.
|
||||
#
|
||||
# Usage:
|
||||
# scripts/mirror-gitea.sh # mirror prod + all tags
|
||||
# GRIDLINE_GITEA_REMOTE=mirror scripts/… # use another remote name
|
||||
# scripts/mirror-gitea.sh --branches # also mirror every local branch
|
||||
|
||||
set -uo pipefail
|
||||
|
||||
GITEA_REMOTE="${GRIDLINE_GITEA_REMOTE:-origin}"
|
||||
MIRROR_BRANCHES=0
|
||||
TRUNK_BRANCH="${GRIDLINE_TRUNK_BRANCH:-prod}"
|
||||
|
||||
for arg in "$@"; do
|
||||
case "$arg" in
|
||||
--branches) MIRROR_BRANCHES=1 ;;
|
||||
-h|--help) sed -n '2,25p' "$0" | sed 's/^# \{0,1\}//'; exit 0 ;;
|
||||
*) echo "mirror-gitea: unknown argument '$arg' (try --help)" >&2; exit 2 ;;
|
||||
esac
|
||||
done
|
||||
|
||||
repo_root="$(git rev-parse --show-toplevel 2>/dev/null)" || {
|
||||
echo "mirror-gitea: not inside a git repository" >&2
|
||||
exit 1
|
||||
}
|
||||
cd "$repo_root" || exit 1
|
||||
|
||||
url="$(git remote get-url "$GITEA_REMOTE" 2>/dev/null || true)"
|
||||
if [ -z "$url" ]; then
|
||||
echo "mirror-gitea: remote '$GITEA_REMOTE' not found — nothing to mirror" >&2
|
||||
exit 1
|
||||
fi
|
||||
|
||||
# Safety: never push to GitHub through this script (it is the source, and a
|
||||
# mis-pointed remote could otherwise rewrite the wrong repository).
|
||||
case "$url" in
|
||||
*github.com*)
|
||||
echo "mirror-gitea: '$GITEA_REMOTE' points at GitHub ($url) — refusing to mirror" >&2
|
||||
exit 1
|
||||
;;
|
||||
esac
|
||||
|
||||
status=0
|
||||
note() { printf 'mirror-gitea: %s\n' "$1" >&2; }
|
||||
|
||||
# Trunk branch. Only mirror it when it exists locally; a fresh clone with just
|
||||
# the feature branch checked out still mirrors the tags below.
|
||||
if git show-ref --verify --quiet "refs/heads/$TRUNK_BRANCH"; then
|
||||
if ! git push "$GITEA_REMOTE" "refs/heads/$TRUNK_BRANCH:refs/heads/$TRUNK_BRANCH"; then
|
||||
status=1
|
||||
note "could not update $TRUNK_BRANCH"
|
||||
fi
|
||||
fi
|
||||
|
||||
# Every local tag, so release tags appear on the mirror too (`--tags` is
|
||||
# idempotent — already-present tags are skipped).
|
||||
if [ -n "$(git tag --list)" ]; then
|
||||
if ! git push "$GITEA_REMOTE" --tags; then
|
||||
status=1
|
||||
note "could not update tags"
|
||||
fi
|
||||
fi
|
||||
|
||||
# Optional: every local branch (off by default — feature branches are ephemeral).
|
||||
if [ "$MIRROR_BRANCHES" = "1" ]; then
|
||||
if ! git push "$GITEA_REMOTE" --all; then
|
||||
status=1
|
||||
note "could not update branches"
|
||||
fi
|
||||
fi
|
||||
|
||||
if [ "$status" = "0" ]; then
|
||||
note "mirror up to date ($url)"
|
||||
fi
|
||||
exit "$status"
|
||||
Reference in New Issue
Block a user