Visitar URL original
feat(ci): open ready-for-review on a clean sync, confirm Sync fork, r… · flexion/open-webui@109f7d2 · GitHub
Skip to content

Commit 109f7d2

Browse files
lgarceau768claude
andcommitted
feat(ci): open ready-for-review on a clean sync, confirm Sync fork, retune cadence
A sync that needed no manual resolution and passed verify now opens its PR ready for review instead of always as a draft; anything else still opens draft. Confirms the "Mirror main from upstream" step's GITHUB_TOKEN push path works (manually verified via Sync fork on 2026-10-08) and keeps main mirrored rather than dropping it. Retunes the schedule from weekly to the 1st/15th of each month to match upstream's actual release cadence. Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
1 parent 3dcfeca commit 109f7d2

2 files changed

Lines changed: 81 additions & 44 deletions

File tree

‎.github/workflows/upstream-sync.yml‎

Lines changed: 63 additions & 31 deletions
Original file line numberDiff line numberDiff line change
@@ -1,9 +1,12 @@
11
name: Upstream Sync
22

33
# Prepares a sync of `flex` onto a new upstream open-webui release and opens a
4-
# DRAFT pull request. It never produces a mergeable result on its own: a human
4+
# pull request. If the merge needed no human conflict resolution and `verify`
5+
# passed, the PR opens ready for review. Otherwise it opens as a DRAFT: a human
56
# resolves the shared-source conflicts, runs the same script locally, and marks
6-
# the PR ready. "Upstream Sync Verify" then re-runs the checks on the PR.
7+
# the PR ready. "Upstream Sync Verify" then re-runs the checks on every push to
8+
# the PR either way, since `verify` proves nothing was silently dropped, not
9+
# that the result builds or that a resolution is semantically right.
710
#
811
# The mechanism lives entirely in scripts/upstream-sync.sh, which is what CI runs
912
# and what a human runs. A failed CI sync is reproduced locally with:
@@ -16,11 +19,12 @@ name: Upstream Sync
1619

1720
on:
1821
schedule:
19-
# Weekly, Monday 09:00 UTC (5am ET). Upstream releases arrive in clusters
20-
# every few weeks, so this bounds sync lag at 7 days while costing ~52
21-
# no-op runs a year. Re-check the cadence if upstream's release rhythm
22-
# changes; do not assume this comment is current.
23-
- cron: '0 9 * * 1'
22+
# 1st and 15th of the month, 09:00 UTC. Upstream releases arrive in
23+
# clusters every 3-4 weeks, so this bounds sync lag at ~14 days while
24+
# costing far fewer no-op runs than a daily or weekly schedule. Re-check
25+
# the cadence if upstream's release rhythm changes; do not assume this
26+
# comment is current.
27+
- cron: '0 9 1,15 * *'
2428
workflow_dispatch:
2529
inputs:
2630
target_tag:
@@ -53,10 +57,11 @@ jobs:
5357
FLEX_REF: origin/flex
5458
OUT_DIR: ${{ github.workspace }}/.upstream-sync-out
5559
# SYNC_TOKEN is optional. See "Token" in the sync runbook in
56-
# README_FLEXION.md: GITHUB_TOKEN is expected to be enough because the
57-
# pushed branch changes no workflow file, but that has not been proven on
58-
# a real run, and a GitHub App installation token is the documented
59-
# fallback if GitHub still rejects the push.
60+
# README_FLEXION.md: GITHUB_TOKEN is sufficient because the pushed branch
61+
# changes no workflow file and main is kept mirrored to upstream (manual
62+
# "Sync fork" confirmed working 2026-10-08), so the push below carries no
63+
# unseen upstream commits. SYNC_TOKEN (a GitHub App installation token) is
64+
# the documented fallback if GitHub still rejects a push.
6065
GH_TOKEN: ${{ secrets.SYNC_TOKEN || github.token }}
6166
# This repo is a fork and the job adds an `upstream` remote. Without
6267
# GH_REPO, `gh` resolves the base repo to the PARENT and every API call
@@ -209,7 +214,7 @@ jobs:
209214
echo "::error title=Push rejected::Could not push ${BRANCH}. If the message above mentions workflow files, GitHub is refusing this token a push touching .github/workflows. The merge does not change that directory, so the cause is upstream commits new to this remote. Fix: open ${GITHUB_SERVER_URL}/${GITHUB_REPOSITORY}/tree/main and click 'Sync fork', then re-run; or add a SYNC_TOKEN secret (GitHub App installation token, or fine-grained PAT with Contents write + Pull requests write + Workflows write) and re-run. To reproduce locally: git switch -c ${BRANCH} origin/flex && scripts/upstream-sync.sh merge ${TARGET}"
210215
exit 1
211216
212-
- name: Open the draft PR
217+
- name: Open the PR
213218
if: steps.decide.outputs.sync == 'true'
214219
id: pr
215220
env:
@@ -220,35 +225,60 @@ jobs:
220225
BASE: ${{ steps.base.outputs.base }}
221226
RUN_URL: ${{ github.server_url }}/${{ github.repository }}/actions/runs/${{ github.run_id }}
222227
run: |
228+
# Conflict-free only if the merge left nothing for a human to resolve
229+
# AND verify confirms nothing Flexion-owned was lost. Either failing
230+
# means a human must look before this is mergeable, so it opens as a
231+
# draft. Clean still isn't a build/boot guarantee -- `verify` can't
232+
# see that -- so the clean body still asks for it before merging.
233+
if [ "${MANUAL_COUNT}" = "0" ] && [ "${VERIFY}" = "success" ]; then
234+
CLEAN=true
235+
else
236+
CLEAN=false
237+
fi
238+
223239
BODY_FILE=$(mktemp)
224240
{
225241
echo "Merges upstream **${TARGET}** into \`flex\` (previous base: **${BASE:-<none>}**)."
226242
echo ""
227-
echo "> [!IMPORTANT]"
228-
echo "> This PR is **not finished**. CI prepared the merge; a human finishes it."
229-
echo "> Merge it with **Create a merge commit** — squash or rebase would strip"
230-
echo "> upstream's history from \`flex\` and make the next sync re-resolve everything."
243+
if [ "${CLEAN}" = "true" ]; then
244+
echo "> [!NOTE]"
245+
echo "> No shared-source files needed manual resolution, and \`upstream-sync.sh verify\`"
246+
echo "> passed. Opened ready for review. **Still run \`npm run build\` and"
247+
echo "> \`docker build .\`** before merging -- \`verify\` proves nothing was silently"
248+
echo "> dropped, not that the result builds."
249+
echo "> Merge it with **Create a merge commit** — squash or rebase would strip"
250+
echo "> upstream's history from \`flex\` and make the next sync re-resolve everything."
251+
else
252+
echo "> [!IMPORTANT]"
253+
echo "> This PR is **not finished**. CI prepared the merge; a human finishes it."
254+
echo "> Merge it with **Create a merge commit** — squash or rebase would strip"
255+
echo "> upstream's history from \`flex\` and make the next sync re-resolve everything."
256+
fi
231257
echo ""
232258
echo "| | |"
233259
echo "|---|---|"
234260
echo "| Files needing human resolution | ${MANUAL_COUNT} |"
235261
echo "| \`upstream-sync.sh verify\` in CI | ${VERIFY} |"
236262
echo "| Run log and remerge diff | [artifact](${RUN_URL}) |"
237263
echo ""
238-
echo "## Finish this sync"
239-
echo ""
240-
echo '```bash'
241-
echo "git fetch origin && git fetch upstream --tags --prune --force"
242-
echo "git switch ${BRANCH}"
243-
echo "# resolve the files listed in the merge commit message, then commit"
244-
echo "scripts/upstream-sync.sh verify ${TARGET}"
245-
echo '```'
246-
echo ""
247-
echo "- [ ] All conflict markers resolved; \`upstream-sync.sh verify\` passes locally"
264+
if [ "${CLEAN}" != "true" ]; then
265+
echo "## Finish this sync"
266+
echo ""
267+
echo '```bash'
268+
echo "git fetch origin && git fetch upstream --tags --prune --force"
269+
echo "git switch ${BRANCH}"
270+
echo "# resolve the files listed in the merge commit message, then commit"
271+
echo "scripts/upstream-sync.sh verify ${TARGET}"
272+
echo '```'
273+
echo ""
274+
echo "- [ ] All conflict markers resolved; \`upstream-sync.sh verify\` passes locally"
275+
fi
248276
echo "- [ ] \`npm run build\` succeeds"
249277
echo "- [ ] \`docker build .\` succeeds"
250278
echo "- [ ] Provider icons, Google OAuth Groups + admin exemption, and the Alembic chain still behave"
251-
echo "- [ ] Mark ready for review"
279+
if [ "${CLEAN}" != "true" ]; then
280+
echo "- [ ] Mark ready for review"
281+
fi
252282
echo ""
253283
cat "${OUT_DIR}/conflict-resolution-log.md"
254284
echo ""
@@ -269,11 +299,13 @@ jobs:
269299
echo "</details>"
270300
} > "$BODY_FILE"
271301
272-
# Always a draft. A sync is never certified by CI alone: `verify`
273-
# proves nothing was silently dropped, not that the result builds or
274-
# that the resolutions are semantically right.
302+
DRAFT_FLAG=()
303+
if [ "${CLEAN}" != "true" ]; then
304+
DRAFT_FLAG=(--draft)
305+
fi
306+
275307
gh pr create \
276-
--draft \
308+
"${DRAFT_FLAG[@]}" \
277309
--base flex \
278310
--head "$BRANCH" \
279311
--title "chore: upstream-sync flex onto ${TARGET}" \

‎README_FLEXION.md‎

Lines changed: 18 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -78,7 +78,7 @@ merge commit, so it needs no arguments and behaves identically in CI and on a fr
7878

7979
#### Option A — Automated Sync (Recommended)
8080

81-
The **Upstream Sync** workflow runs weekly (Mondays, 09:00 UTC) and can be run from
81+
The **Upstream Sync** workflow runs on the 1st and 15th of each month (09:00 UTC) and can be run from
8282
**Actions → Upstream Sync → Run workflow** with an optional `target_tag`. When upstream has a
8383
`v*.*.*` release newer than the one `flex` is on, it:
8484

@@ -90,13 +90,15 @@ The **Upstream Sync** workflow runs weekly (Mondays, 09:00 UTC) and can be run f
9090
- lock files → upstream's, flagged for regeneration
9191
- everything else → conflict markers committed as-is for a human
9292
2. Runs `upstream-sync.sh verify`, uploads the log and the `--remerge-diff` as a run artifact.
93-
3. Pushes the branch and opens a **draft** PR into `flex`.
93+
3. Pushes the branch and opens a PR into `flex`.
9494

95-
**The PR is always a draft, and CI never marks it ready.** `verify` proves nothing was silently
96-
dropped; it does not prove the result builds or that the resolutions are semantically right. A
97-
human finishes every sync.
95+
**The PR opens ready for review only if nothing needed manual resolution and `verify` passed.**
96+
Otherwise it opens as a **draft**, and CI never marks it ready on its own. Either way, `verify`
97+
proves nothing was silently dropped; it does not prove the result builds or that the resolutions
98+
are semantically right — run `npm run build` and `docker build .` before merging even a clean
99+
sync.
98100

99-
**Finishing a sync:**
101+
**Finishing a sync that needed manual resolution:**
100102

101103
```bash
102104
git fetch origin && git fetch upstream --tags --prune --force
@@ -114,13 +116,15 @@ fire on `main`/`dev`). Merge with **Create a merge commit**, then publish to ECR
114116
**Token.** The workflow uses `GITHUB_TOKEN` by default. GitHub refuses a GitHub App token any
115117
push that creates or updates a file under `.github/workflows/`, and there is no `workflows`
116118
entry in a workflow's `permissions:` block to grant. This design sidesteps that by never
117-
changing that directory in a sync. If a push is still rejected (upstream commits that are new to
118-
this remote also carry workflow files), the run says so and offers two fixes: click **Sync fork**
119-
on `main` so those commits already exist here, or add a `SYNC_TOKEN` secret — a GitHub App
120-
installation token, or a fine-grained PAT with *Contents: write*, *Pull requests: write*,
121-
*Workflows: write* — which the workflow uses automatically when present. A classic PAT with the
122-
`workflow` scope also works but needs an org owner to authorize it, which is what stalled the
123-
earlier `UPSTREAM_SYNC_TOKEN` attempt.
119+
changing that directory in a sync, and by keeping `main` mirrored to upstream before merging
120+
(the "Mirror main from upstream" step calls the `merge-upstream` API; **confirmed working via a
121+
manual "Sync fork" click on 2026-10-08**), so the upstream commits a sync merges already exist on
122+
this remote and the push carries no new workflow-touching commits. If a push is still rejected,
123+
the run says so and offers two fixes: click **Sync fork** on `main` and re-run, or add a
124+
`SYNC_TOKEN` secret — a GitHub App installation token, or a fine-grained PAT with *Contents:
125+
write*, *Pull requests: write*, *Workflows: write* — which the workflow uses automatically when
126+
present. A classic PAT with the `workflow` scope also works but needs an org owner to authorize
127+
it, which is what stalled the earlier `UPSTREAM_SYNC_TOKEN` attempt.
124128

125129
---
126130

@@ -141,6 +145,7 @@ scripts/upstream-sync.sh verify
141145
git push -u origin upstream-sync/vX.Y.Z
142146
gh pr create --draft --base flex --head upstream-sync/vX.Y.Z \
143147
--title "chore: upstream-sync flex onto vX.Y.Z"
148+
# drop --draft if the merge needed no manual resolution and verify passed
144149
```
145150

146151
Never force-push `flex`, and never rebase it onto a release.

0 commit comments

Comments
 (0)