Skip to content
This repository was archived by the owner on Aug 17, 2026. It is now read-only.
41 changes: 41 additions & 0 deletions .github/workflows/ci-zge.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,41 @@
# ZGEnergy fork gates — required for main protection once registered as a check.
name: ci-zge

on:
pull_request:
branches: [main, develop]
push:
branches: [main, develop]
workflow_dispatch:

concurrency:
group: ci-zge-${{ github.workflow }}-${{ github.event.pull_request.number || github.ref }}
cancel-in-progress: true

jobs:
zge-gates:
name: zge-gates
runs-on: ubuntu-latest
timeout-minutes: 30
steps:
- name: Checkout
uses: actions/checkout@v4

- name: Setup Node
uses: actions/setup-node@v4
with:
node-version: "22.22"
cache: npm

- name: Install
env:
ELECTRON_SKIP_BINARY_DOWNLOAD: "1"
run: |
npm ci || npm install --force

- name: Structural + focused tests + build
env:
ELECTRON_SKIP_BINARY_DOWNLOAD: "1"
run: |
chmod +x scripts/upstream-sync.sh
scripts/upstream-sync.sh verify
172 changes: 172 additions & 0 deletions .github/workflows/upstream-sync.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,172 @@
# Keep exactly one open upstream-sync PR: stable branch force-updated each run.
name: upstream-sync

on:
schedule:
# Mondays 15:00 UTC
- cron: "0 15 * * 1"
workflow_dispatch:
inputs:
upstream_ref:
description: Upstream branch to merge
required: false
default: develop
dry_run:
description: If true, only print status
required: false
default: "false"

permissions:
contents: write
pull-requests: write

concurrency:
group: upstream-sync
cancel-in-progress: false

jobs:
sync:
runs-on: ubuntu-latest
timeout-minutes: 45
env:
UPSTREAM_REF: ${{ github.event.inputs.upstream_ref || 'develop' }}
GH_REPO: ${{ github.repository }}
# Stable branch — each run force-with-lease updates the same PR head
SYNC_BRANCH: sync/upstream-${{ github.event.inputs.upstream_ref || 'develop' }}
steps:
- name: Checkout main
uses: actions/checkout@v4
with:
ref: main
fetch-depth: 0
token: ${{ secrets.GITHUB_TOKEN }}

- name: Configure git identity
run: |
git config user.name "github-actions[bot]"
git config user.email "41898282+github-actions[bot]@users.noreply.github.com"

- name: Add upstream remote
run: |
git remote add upstream https://github.com/BlackBeltTechnology/pi-agent-dashboard.git \
|| git remote set-url upstream https://github.com/BlackBeltTechnology/pi-agent-dashboard.git
git fetch upstream "$UPSTREAM_REF" --prune
git fetch origin main --prune

- name: Status
id: status
run: |
chmod +x scripts/upstream-sync.sh
scripts/upstream-sync.sh status | tee /tmp/upstream-status.txt
BEHIND=$(git rev-list --count "origin/main..upstream/${UPSTREAM_REF}")
echo "behind=$BEHIND" >> "$GITHUB_OUTPUT"
SHA=$(git rev-parse --short "upstream/${UPSTREAM_REF}")
echo "usha=$SHA" >> "$GITHUB_OUTPUT"

- name: Skip if up to date
if: steps.status.outputs.behind == '0'
run: echo "Already up to date with upstream."

- name: Dry run only
if: github.event_name == 'workflow_dispatch' && github.event.inputs.dry_run == 'true'
run: echo "dry_run=true; stopping before merge"

- name: Merge + upsert single PR
if: steps.status.outputs.behind != '0' && !(github.event_name == 'workflow_dispatch' && github.event.inputs.dry_run == 'true')
env:
GH_TOKEN: ${{ secrets.GITHUB_TOKEN }}
run: |
set +e
scripts/upstream-sync.sh merge
rc=$?
set -e
echo "merge_rc=$rc"
if [[ $rc -eq 0 ]]; then
scripts/upstream-sync.sh pr
exit 0
fi
if [[ $rc -ne 2 ]]; then
exit $rc
fi

# Conflicts remain: publish help on the same stable branch (still one PR),
# close any other sync PRs, leave draft for human/agent resolution.
# Persist the just-written inventory outside the worktree before merge --abort
# (abort drops newly staged/uncommitted conflict report files).
DATE_TAG=$(date -u +%Y%m%d)
REPORT_REL="docs/upstream-sync-conflicts-${DATE_TAG}.md"
REPORT_TMP="/tmp/upstream-sync-conflicts-${DATE_TAG}.md"
if [[ -f "$REPORT_REL" ]]; then
cp -f "$REPORT_REL" "$REPORT_TMP"
elif [[ -f "$(git rev-parse --git-path MERGE_HEAD 2>/dev/null || true)" ]] || git rev-parse -q --verify MERGE_HEAD >/dev/null 2>&1; then
# Fallback: rebuild inventory from remaining unmerged paths before abort
{
echo "# Upstream sync conflicts (${DATE_TAG})"
echo
echo "- upstream: ${UPSTREAM_REF}@${{ steps.status.outputs.usha }}"
echo "- branch: ${SYNC_BRANCH}"
echo
echo "## Remaining unmerged paths"
echo
git diff --name-only --diff-filter=U | sed 's/^/- /'
} >"$REPORT_TMP"
fi
git merge --abort || true
git checkout -B "$SYNC_BRANCH" origin/main
mkdir -p docs
{
echo "# Upstream sync needs human/agent merge"
echo
echo "Upstream ${UPSTREAM_REF} @ ${{ steps.status.outputs.usha }} is **${{ steps.status.outputs.behind }}** commits ahead of main."
echo
echo "Automation keeps **one** open PR on ${SYNC_BRANCH} (this branch is force-updated)."
echo
echo "Resolve with:"
echo
echo '```bash'
echo "git fetch upstream && scripts/upstream-sync.sh merge"
echo "# resolve remaining paths, then:"
echo "scripts/upstream-sync.sh verify && scripts/upstream-sync.sh pr"
echo '```'
echo
if [[ -f "$REPORT_TMP" ]]; then
echo "## Conflict inventory"
echo
cat "$REPORT_TMP"
fi
} > docs/upstream-sync-help.md
git add docs/upstream-sync-help.md
if [[ -f "$REPORT_TMP" ]]; then
cp -f "$REPORT_TMP" "$REPORT_REL"
git add "$REPORT_REL"
fi
git commit -m "chore(sync): conflict help for ${UPSTREAM_REF}@${{ steps.status.outputs.usha }}"
git push --force-with-lease -u origin "$SYNC_BRANCH"

# Close other sync PRs; upsert this one as draft
KEEP_HEAD="$SYNC_BRANCH" GH_REPO_VAL="$GH_REPO" python3 - <<'PY'
import json, os, subprocess
repo = os.environ["GH_REPO_VAL"]
keep = os.environ["KEEP_HEAD"]
raw = subprocess.check_output(["gh","pr","list","-R",repo,"--state","open","--limit","100","--json","number,headRefName,labels"], text=True)
for pr in json.loads(raw):
head = pr.get("headRefName") or ""
labels = {(l.get("name") if isinstance(l, dict) else l) for l in (pr.get("labels") or [])}
if head == keep:
continue
if not head.startswith("sync/upstream"):
continue
n = str(pr["number"])
body = f"Superseded by latest automated sync on `{keep}`."
subprocess.run(["gh","pr","close",n,"-R",repo,"--comment",body], check=False)
PY

existing=$(gh pr list -R "$GH_REPO" --state open --head "$SYNC_BRANCH" --json number --jq '.[0].number // empty' || true)
TITLE="chore(sync): CONFLICTS upstream ${UPSTREAM_REF} @ ${{ steps.status.outputs.usha }}"
if [[ -n "$existing" ]]; then
gh pr edit "$existing" -R "$GH_REPO" --title "$TITLE" --body-file docs/upstream-sync-help.md || true
gh pr ready "$existing" -R "$GH_REPO" --undo 2>/dev/null || true
else
gh pr create -R "$GH_REPO" --base main --head "$SYNC_BRANCH" --draft \
--title "$TITLE" --label upstream-sync --body-file docs/upstream-sync-help.md
fi
6 changes: 5 additions & 1 deletion README.md
Original file line number Diff line number Diff line change
Expand Up @@ -982,12 +982,16 @@ smallest possible delta (it still tracks upstream). Changes vs upstream:
Opinionated secure self-host (recommended): see **[`deploy/`](deploy/)** —

```bash
curl -fsSL https://raw.githubusercontent.com/ZGEnergy/omp-dashboard/omp-minimal/deploy/install.sh | bash
curl -fsSL https://raw.githubusercontent.com/ZGEnergy/omp-dashboard/main/deploy/install.sh | bash
```

For the plain/manual route, use upstream's Quickstart paths above (point them at
`omp`, and build from source since the fork isn't published to npm).

### Tracking upstream

We merge BlackBelt `develop` on a schedule via `scripts/upstream-sync.sh` and `.github/workflows/upstream-sync.yml`. Policy, protected paths, and gates: [`docs/upstream-sync.md`](docs/upstream-sync.md).

### Known gaps

- PWA "install"/`manifest.json` over a zrok tunnel 503s (zrok-edge OAuth intercepts
Expand Down
4 changes: 2 additions & 2 deletions deploy/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -18,15 +18,15 @@ zrok tunnel running as systemd **user** services that survive reboot.
## Install

```bash
curl -fsSL https://raw.githubusercontent.com/ZGEnergy/omp-dashboard/omp-minimal/deploy/install.sh | bash
curl -fsSL https://raw.githubusercontent.com/ZGEnergy/omp-dashboard/main/deploy/install.sh | bash
```

Run it in a real terminal — it prompts for your zrok token, a share name, and your
`@zerogcapital.com` email. If you pipe it and stdin isn't a TTY (so the prompts
can't read), download and run it directly instead:

```bash
curl -fsSLo install.sh https://raw.githubusercontent.com/ZGEnergy/omp-dashboard/omp-minimal/deploy/install.sh
curl -fsSLo install.sh https://raw.githubusercontent.com/ZGEnergy/omp-dashboard/main/deploy/install.sh
bash install.sh
```

Expand Down
2 changes: 1 addition & 1 deletion deploy/install.sh
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,7 @@ set -euo pipefail
PREFIX="${PREFIX:-$HOME/.omp-dashboard}"
LOCAL_BIN="$HOME/.local/bin"
REPO_URL="https://github.com/ZGEnergy/omp-dashboard.git"
REF="${OMP_DASH_REF:-omp-minimal}"
REF="${OMP_DASH_REF:-main}"

# ── Bootstrap for `curl … | bash` ────────────────────────────────────────────
# When piped over stdin there is no sibling lib.sh/templates, and stdin is the
Expand Down
63 changes: 63 additions & 0 deletions docs/upstream-sync.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,63 @@
# Upstream sync (ZGEnergy fork)

This repo is a fork of [`BlackBeltTechnology/pi-agent-dashboard`](https://github.com/BlackBeltTechnology/pi-agent-dashboard).
We track upstream **`develop`** while shipping ZGE-only product on protected **`main`**.

## Source of truth

| Branch | Role |
|--------|------|
| `main` | Default, **protected**. Production promote target. |
| `develop` | Fast-forward **mirror** of `main` (legacy clients / install refs). |
| `upstream/develop` | BlackBelt integration branch we merge from. |
| `sync/upstream-develop` | **Stable** sync branch (force-updated). Only **one** open sync PR is kept; older ones are closed as superseded. |

## Commands

```bash
# remotes: origin = ZGEnergy/omp-dashboard, upstream = BlackBeltTechnology/pi-agent-dashboard
scripts/upstream-sync.sh status
scripts/upstream-sync.sh merge # branch + merge; auto --ours on protected paths
scripts/upstream-sync.sh verify # Gate 0 structure + focused vitest + build
scripts/upstream-sync.sh pr # force-with-lease push + upsert **one** PR; close older sync PRs
scripts/upstream-sync.sh ff-develop # point origin/develop at origin/main
```

Env knobs: `UPSTREAM_REF`, `TARGET_BRANCH`, `SYNC_BRANCH`, `DRY_RUN=1`, `SKIP_BUILD=1`, `SYNC_ADOPT_UPSTREAM=1`.

Weekly automation: [`.github/workflows/upstream-sync.yml`](../.github/workflows/upstream-sync.yml) — always leaves **at most one** open `upstream-sync` PR (latest).
ZGE CI gates: [`.github/workflows/ci-zge.yml`](../.github/workflows/ci-zge.yml)

## Protected paths (ZGE wins on conflict by default)

- `deploy/**` — self-host installer, systemd, zrok
- `packages/server/src/push/**`, `routes/push-routes.ts` — Web Push
- `packages/server/src/routes/omp-config-routes.ts` — OMP settings mirror
- `packages/shared/src/omp-agent-paths.ts`, `input-needed-tools.ts` (+ their tests)
- `docs/upstream-sync.md`, `scripts/upstream-sync.sh`, `.github/workflows/ci-zge.yml`, `upstream-sync.yml`

Shared hubs (`packages/server/src/server.ts`, `packages/extension/src/bridge.ts`, `packages/shared/src/config.ts`) need **semantic** merges: keep both OMP/push call sites and upstream features.

## Gates

0. **Structural** — deploy/, push/, omp-agent-paths, install.sh still points at `ZGEnergy/omp-dashboard`
1. **Focused vitest** (Node ≥ 22.18, prefer 22.22) — omp-agent-paths, config-push, push payload/dispatcher/vapid/classifier, ws-ticket when present
2. **Build** — `ELECTRON_SKIP_BINARY_DOWNLOAD=1 npm run build`
3. **Runtime smoke** (manual/validation host) — health + vapid with push enabled; omp session spawn
4. **Prod promote** — explicit only (`~/.omp-dashboard` checkout + systemd); never from the Action

## Conflict playbook

1. Run `merge`; read `docs/upstream-sync-conflicts-*.md` if present.
2. Protected paths already `--ours` unless `SYNC_ADOPT_UPSTREAM=1`.
3. For each remaining file: open both stages, combine imports/registrations, re-run tests for that area.
4. `git commit` to finish the merge if needed → `verify` → `pr`.
5. Land via normal protected-main review (1 approval + `ci-zge`).

## Installer default ref

`deploy/install.sh` uses `OMP_DASH_REF` default **`main`**. Override for experiments:

```bash
OMP_DASH_REF=develop bash deploy/install.sh
```
Loading
Loading