LibreChat/.github/workflows/gitnexus-pr-command.yml
Marco Beretta 9e6d677751
⚙️ ci: Bump GitHub Actions to Node.js 24 Runtimes (#14689)
* ci: bump GitHub Actions to Node.js 24 runtimes

Clear Node 20 deprecation warnings on runners by moving workflow
actions to majors that declare node24 (checkout, cache, setup-node,
artifacts, Docker buildx/build-push/qemu/login, github-script,
setup-go, Azure login/helm, create-pull-request, axe-linter).

* fix: release leader lock via ioredis on Redis Cluster

@keyv/redis EVAL can surface unhandled MOVED redirects on cluster,
so resign() logged failure and left LeadingServerUUID set. Use ioredis
for leader election SET NX / GET / Lua (same pattern as principals and
concurrency locks) so cluster redirects are retried and resignation
clears the lock.
2026-08-07 11:25:29 -04:00

141 lines
5.7 KiB
YAML

# Responds to `/gitnexus index` comments on pull requests.
#
# Gated to the same author_association roles (OWNER, MEMBER, COLLABORATOR)
# as the automatic PR index trigger, but applied to the COMMENTER, not
# the PR author. This intentionally lets a contributor index a PR from
# a non-contributor / first-time fork author — the contributor takes
# responsibility for the trust boundary by typing the command.
#
# When a matching comment lands on a PR, this workflow dispatches
# `gitnexus-index.yml` with the PR number and the `refs/pull/<N>/head`
# ref so indexing works for fork PRs too (GitHub mirrors every PR's
# head ref into the base repo regardless of which fork it originated
# from, so actions/checkout can always resolve it).
#
# Use cases:
# - Re-index a PR after a rebase without pushing a new commit
# - Index a docs-only PR that was skipped by paths-ignore
# - Index a non-contributor (fork) PR that the auto-trigger skipped
# - Re-run a failed index
#
# Supported commands:
# /gitnexus index — index the PR with embeddings (default)
# /gitnexus index embeddings — explicit form of the above; same effect
# /gitnexus index fast — graph-only index (skip embeddings), for
# a quick re-index without waiting ~5 min
# of embedding generation
name: GitNexus PR Command
on:
issue_comment:
types: [created]
permissions:
contents: read
pull-requests: write
actions: write # needed to dispatch gitnexus-index.yml
concurrency:
group: gitnexus-pr-command-${{ github.event.issue.number }}
cancel-in-progress: false
jobs:
dispatch:
# Only run for PR comments that start with /gitnexus from trusted
# commenters. Intentionally checks the COMMENTER's association so a
# contributor can index a non-contributor's PR on demand.
if: |
github.event.issue.pull_request != null &&
startsWith(github.event.comment.body, '/gitnexus') &&
(github.event.comment.author_association == 'OWNER' ||
github.event.comment.author_association == 'MEMBER' ||
github.event.comment.author_association == 'COLLABORATOR')
runs-on: ubuntu-latest
timeout-minutes: 5
steps:
- name: Parse command and resolve PR head ref
id: parse
uses: actions/github-script@v8
with:
script: |
const body = context.payload.comment.body.trim();
const match = body.match(/^\/gitnexus\s+(\w+)(?:\s+(\w+))?/);
if (!match) {
core.setFailed(`Unrecognized command: ${body}. Try: /gitnexus index [fast]`);
return;
}
const [, subcommand, modifier] = match;
if (subcommand !== 'index') {
core.setFailed(`Unknown subcommand: ${subcommand}. Only 'index' is supported.`);
return;
}
// Default to embeddings on — a contributor typing the command
// has already decided they want a full re-index. The `fast`
// modifier is the explicit opt-out for graph-only runs.
// `embeddings` is accepted as a no-op alias for backwards
// compat with the previous command form.
let embeddings = 'true';
if (modifier === 'fast' || modifier === 'graph-only' || modifier === 'no-embeddings') {
embeddings = 'false';
}
// Use refs/pull/<N>/head instead of the raw head SHA. GitHub
// mirrors every PR's head into the base repo as this ref, so
// actions/checkout can always resolve it — even for PRs from
// forks whose raw SHAs don't exist in the base repo.
const prNum = context.payload.issue.number;
core.setOutput('pr_number', String(prNum));
core.setOutput('pr_ref', `refs/pull/${prNum}/head`);
core.setOutput('embeddings', embeddings);
core.info(
`Dispatching index for PR #${prNum} at refs/pull/${prNum}/head (embeddings=${embeddings}, modifier=${modifier || '(none)'})`,
);
- name: Dispatch gitnexus-index workflow
uses: actions/github-script@v8
env:
EMBEDDINGS: ${{ steps.parse.outputs.embeddings }}
PR_NUMBER: ${{ steps.parse.outputs.pr_number }}
PR_REF: ${{ steps.parse.outputs.pr_ref }}
with:
script: |
const prNumber = process.env.PR_NUMBER || '';
const prRef = process.env.PR_REF || '';
const embeddings = process.env.EMBEDDINGS || 'false';
if (!/^[0-9]+$/.test(prNumber)) {
core.setFailed(`Invalid PR number: ${prNumber}`);
return;
}
if (prRef !== `refs/pull/${prNumber}/head`) {
core.setFailed(`Invalid PR ref: ${prRef}`);
return;
}
if (!['true', 'false'].includes(embeddings)) {
core.setFailed(`Invalid embeddings value: ${embeddings}`);
return;
}
await github.rest.actions.createWorkflowDispatch({
owner: context.repo.owner,
repo: context.repo.repo,
workflow_id: 'gitnexus-index.yml',
ref: 'main',
inputs: {
pr_number: prNumber,
pr_ref: prRef,
embeddings,
force: 'false',
deploy_after: 'true',
},
});
- name: React to the comment
uses: actions/github-script@v8
with:
script: |
await github.rest.reactions.createForIssueComment({
owner: context.repo.owner,
repo: context.repo.repo,
comment_id: context.payload.comment.id,
content: 'rocket',
});