mirror of
https://github.com/danny-avila/LibreChat.git
synced 2026-11-01 19:46:50 +00:00
* 🎯 ci: Retarget Pull Requests Opened Against `main` to `dev` Contributions land on `dev`; `main` only moves at release time. With `main` as the repository's default branch, pull requests default to it, and the mismatch has been resolved by hand at merge time. Adds a `pull_request_target` hook that switches a newly opened or reopened pull request's base from `main` to `dev` and posts a one-time explanation, plus a manual `workflow_dispatch` sweep (dry-run by default) for the existing backlog. Release-bound upstream branches keep `main` as their base: `dev`, `release/*`, `hotfix/*`, `backport/*` and `*-main`. The `target: main` label exempts any pull request. The exemptions apply only to branches on this repository, so a fork branch that happens to match one of those names is still retargeted. The job checks out the trusted base commit rather than the pull request head, so the write-scoped token never runs contributor code. `pull_request_target` reads its workflow definition from the base branch, so this must exist on `main` to take effect. * 📝 docs: Record the `dev`-First Branch Policy for Agents `CLAUDE.md` and `AGENTS.md` never stated which branch work targets, and the default branch is `main` — so `gh pr create` and the GitHub UI point at `main` unless told otherwise, and an agent following the defaults gets it wrong. States the policy, the reason a backport to `main` is never needed (`main` is kept as a strict ancestor of `dev`), and the exemptions the retarget workflow honors. * 📐 docs: Record Typecheck, Formatting and Issue-Closing Gotchas for Agents Four repeat cycle-wasters that neither file stated, each verified against the current tree: - `tsdown` emits without typechecking, so `npm run build` proves nothing about types in `packages/api`, `packages/client` or `packages/data-schemas`; only `packages/data-provider` runs `tsc` in its build. - `packages/client/tsconfig.json` excludes `*.spec.ts(x)` and `*.test.ts(x)`, so type errors in its specs surface only at run time. - `npm run sort-imports` with no arguments rewrites all six source roots. - `Fixes #N` never closes an issue here, because closing keywords are honored only on merges into the default branch and everything merges to `dev`. Also notes that worktrees share one stash stack, and points at `npm run static-checks -- --against origin/dev` as the local mirror of CI. * 🔁 fix: Address Codex Review on the Retarget Workflow - **Backport branches are no longer exempt.** Exempting `backport/*` and `*-main` contradicted the invariant the policy rests on: `main` is a fast-forward of `dev`, so a backport merged straight to `main` is precisely what breaks it. `target: main` remains the deliberate, explicit escape hatch; `dev`, `release/*` and `hotfix/*` stay exempt as release-bound branches. - **`npm run update` does not sync `dev`.** `config/update.js` hard-codes `git checkout main` and `git pull origin main` — it is the self-host deployment updater, not a branch-sync tool. Telling contributors to refresh `dev` with it would have branched them off `main` and recreated the very flow this change exists to prevent. Replaced with the explicit git commands. - **A dry run no longer reports work it did not do.** The summary said "Retargeted N" on a dispatch that wrote nothing, which could read as a completed sweep. - **A failed explanation comment is recorded, not swallowed.** The base edit has already landed at that point, so a later run would skip the pull request and the contributor would never receive the note. It now counts, fails the run, and names the retry: `EXPLAIN_MISSING=true`, which posts a missing explanation onto an already-retargeted pull request. - **The sweep paginates.** `gh pr list --limit 500` is a cap, not a page size, and would have silently ignored the remainder while reporting success. Verified against the stubbed `gh` boundary: `*-main` now retargets, the dry run performs zero writes and says so, a failing comment yields a non-zero exit, and `EXPLAIN_MISSING` posts onto a pull request already based on `dev`. The paginated query returns all 258 open pull requests based on `main`. * 🧷 fix: Address the Second Codex Round on the Retarget Workflow - **`already_explained` now separates "lookup failed" from "no marker".** A transient comments-API failure returned the same status as a clean miss, so the run would post a second explanation and report success — defeating the deduplication it exists for. A failed read now declines to post and is recorded. - **`CONTRIBUTING.md` syncs from the canonical remote.** The previous fix pulled `dev` from `origin`, which in the fork-based flow described two sections later is the contributor's own fork — so it would fail outright or silently sync a stale branch, landing them on the wrong base again. - **The hook retries on `synchronize`.** If the `opened` run failed before `gh pr edit` landed, nothing retried and the pull request stayed on `main`. A retargeted pull request no longer matches `branches: [main]`, so this adds no redundant runs — and the existing backlog now drains on contributor activity rather than only by sweep. - **Sweeps serialize against each other** via a shared concurrency group, while the hook stays keyed per pull request. Deliberately not one global group: GitHub keeps a single pending job per group, so a burst of openings would cancel queued retargets — trading a rare duplicate comment for dropped retargets is the wrong way round. - **`main` is an ancestor of `dev`, not a strict one.** Right after a sync the two refs are equal, which the previous wording described as a violated invariant. Verified: a failing comments lookup edits the base but posts nothing and exits non-zero.
79 lines
2.5 KiB
YAML
79 lines
2.5 KiB
YAML
name: Retarget PRs to dev
|
|
|
|
on:
|
|
pull_request_target:
|
|
types: [opened, reopened, synchronize]
|
|
branches: [main]
|
|
workflow_dispatch:
|
|
inputs:
|
|
dry_run:
|
|
description: 'Report what would change without editing any pull request'
|
|
type: boolean
|
|
default: true
|
|
pr_numbers:
|
|
description: 'Space-separated PR numbers (default: every open pull request based on main)'
|
|
required: false
|
|
default: ''
|
|
|
|
permissions:
|
|
contents: read
|
|
pull-requests: write
|
|
|
|
# Per-pull-request for the hook so a burst of openings runs in parallel; GitHub keeps only one
|
|
# pending job per group, so a single shared group would cancel queued retargets. Every sweep shares
|
|
# one group so two of them cannot process the same pull request at once.
|
|
concurrency:
|
|
group: pr-retarget-dev-${{ github.event.pull_request.number || 'sweep' }}
|
|
cancel-in-progress: false
|
|
|
|
jobs:
|
|
on-open:
|
|
name: Retarget on open
|
|
if: github.event_name == 'pull_request_target'
|
|
runs-on: ubuntu-latest
|
|
timeout-minutes: 5
|
|
steps:
|
|
- uses: actions/checkout@v5
|
|
with:
|
|
# pull_request_target runs with a write token: check out the trusted base
|
|
# commit only, never the pull request head.
|
|
ref: ${{ github.event.pull_request.base.sha }}
|
|
persist-credentials: false
|
|
sparse-checkout: .github/scripts
|
|
|
|
- name: Retarget onto dev
|
|
env:
|
|
GH_TOKEN: ${{ secrets.GITHUB_TOKEN }}
|
|
REPO: ${{ github.repository }}
|
|
PR_NUMBER: ${{ github.event.pull_request.number }}
|
|
run: .github/scripts/retarget-prs.sh "$PR_NUMBER"
|
|
|
|
sweep:
|
|
name: Sweep open pull requests
|
|
if: github.event_name == 'workflow_dispatch'
|
|
runs-on: ubuntu-latest
|
|
timeout-minutes: 60
|
|
steps:
|
|
- uses: actions/checkout@v5
|
|
with:
|
|
persist-credentials: false
|
|
sparse-checkout: .github/scripts
|
|
|
|
- name: Retarget onto dev
|
|
env:
|
|
GH_TOKEN: ${{ secrets.GITHUB_TOKEN }}
|
|
REPO: ${{ github.repository }}
|
|
DRY_RUN: ${{ inputs.dry_run }}
|
|
PR_NUMBERS: ${{ inputs.pr_numbers }}
|
|
THROTTLE_SECONDS: '2'
|
|
run: |
|
|
numbers="$PR_NUMBERS"
|
|
if [ -z "$numbers" ]; then
|
|
numbers="$(gh api --paginate "repos/$REPO/pulls?base=main&state=open&per_page=100" --jq '.[].number')"
|
|
fi
|
|
if [ -z "$numbers" ]; then
|
|
echo "No open pull requests based on main."
|
|
exit 0
|
|
fi
|
|
# shellcheck disable=SC2086
|
|
.github/scripts/retarget-prs.sh $numbers
|