mirror of
https://github.com/danny-avila/LibreChat.git
synced 2026-05-13 16:07:30 +00:00
💬 feat: Serialize GitNexus Deploys and Post Completion Comments on PR Commands (#12623)
Three related changes that tighten the GitNexus CI/CD loop. Serialized deploys - Previous concurrency group was keyed by head ref with cancel-in-progress, which let deploys targeting different refs (e.g. main push + PR command) run in parallel. That's a data race: the prune-stale-indexes step computes active_names up front, so deploy A rsyncing /opt/gitnexus/indexes/LibreChat-pr-12580 can collide with deploy B pruning the same folder based on a pre-rsync view of the active set. - Collapse to a single global group gitnexus-deploy with cancel-in-progress: false. All deploys queue behind one another. A rsync/docker-compose restart is never killed mid-operation. The 20-minute job timeout bounds queue depth. PR completion feedback - Add a "index complete" comment step in gitnexus-index.yml that fires only when inputs.pr_number is set (i.e. the run came via the /gitnexus command). Posts success or failure with a link to the run and whether embeddings were generated. - Add a "deploy complete" comment step in gitnexus-deploy.yml that handles both trigger paths: workflow_run from a native PR auto-index (PR number recovered from the matrix entry whose runId matches the trigger run), and workflow_dispatch from the index workflow's bot- fallback path (PR number passed through as a new inputs.pr_number). - Plumb inputs.pr_number through the bot-fallback dispatch in gitnexus-index.yml so the deploy workflow knows where to comment for command-triggered runs. - Only comments on the PR that asked for the index, never broadcasts. Workflow rename - Drop the "DigitalOcean" suffix from the deploy workflow's display name and filename. The platform is still DO (.do/gitnexus/ still holds the compose + caddy config) but the workflow itself is platform-agnostic in form and the suffix was visual noise. - File renamed gitnexus-deploy-do.yml -> gitnexus-deploy.yml. - Concurrency group and all cross-references updated in lock-step. - permissions at deploy job level now includes pull-requests: write so the completion comment can post.
This commit is contained in:
parent
8cb5c62fa1
commit
546f006e42
2 changed files with 129 additions and 13 deletions
|
|
@ -1,4 +1,4 @@
|
|||
# Deploys GitNexus indexes to a DigitalOcean droplet via SSH + rsync.
|
||||
# Deploys GitNexus indexes to a droplet via SSH + rsync.
|
||||
#
|
||||
# Architecture:
|
||||
# GitHub Actions (deploy)
|
||||
|
|
@ -49,26 +49,39 @@
|
|||
# GITNEXUS_DO_KNOWN_HOST — output of `ssh-keyscan -H <host>` pinning the
|
||||
# droplet's host keys (prevents MITM/TOFU risk)
|
||||
|
||||
name: GitNexus Deploy (DigitalOcean)
|
||||
name: GitNexus Deploy
|
||||
|
||||
on:
|
||||
workflow_run:
|
||||
workflows: ['GitNexus Index']
|
||||
types: [completed]
|
||||
workflow_dispatch:
|
||||
inputs:
|
||||
pr_number:
|
||||
description: 'Optional PR number to post completion comment on (set by bot-triggered dispatches from gitnexus-index.yml)'
|
||||
type: string
|
||||
default: ''
|
||||
|
||||
permissions:
|
||||
actions: read
|
||||
contents: read
|
||||
pull-requests: read
|
||||
pull-requests: write # post completion comments on served PR indexes
|
||||
|
||||
# Per-ref concurrency: rapid pushes to the same branch/PR coalesce, but
|
||||
# deploys targeting different refs can still run in parallel. Safe because
|
||||
# each deploy only rsync's its own folder; final step always reflects the
|
||||
# latest state for all refs discovered at resolve time.
|
||||
# Global serialization. Earlier versions used per-ref concurrency with
|
||||
# cancel-in-progress so rapid pushes to the same ref coalesced but deploys
|
||||
# targeting different refs ran in parallel. That had a data race: the
|
||||
# prune-stale-indexes step computes its active_names up front, so if
|
||||
# deploy A is rsyncing /opt/gitnexus/indexes/LibreChat-pr-12580 while
|
||||
# deploy B (started slightly later with a different ref) prunes, B can
|
||||
# rm -rf a folder A is still uploading into.
|
||||
#
|
||||
# All deploys now queue behind a single group. cancel-in-progress is
|
||||
# false so a running rsync/docker-compose restart never gets killed
|
||||
# mid-operation (which would leave the droplet in a partial state).
|
||||
# The 20-minute job timeout bounds total queue depth.
|
||||
concurrency:
|
||||
group: gitnexus-deploy-do-${{ github.event.workflow_run.head_branch || github.ref }}
|
||||
cancel-in-progress: true
|
||||
group: gitnexus-deploy
|
||||
cancel-in-progress: false
|
||||
|
||||
env:
|
||||
GITNEXUS_VERSION: '1.5.3'
|
||||
|
|
@ -145,7 +158,7 @@ jobs:
|
|||
permissions:
|
||||
actions: read
|
||||
contents: read
|
||||
pull-requests: read
|
||||
pull-requests: write # post deploy-complete comments on served PR indexes
|
||||
steps:
|
||||
- name: Checkout deploy config
|
||||
uses: actions/checkout@v4
|
||||
|
|
@ -450,6 +463,70 @@ jobs:
|
|||
docker compose logs --tail 30 gitnexus || true
|
||||
REMOTE
|
||||
|
||||
# When the deploy was triggered by a PR command path, post a
|
||||
# terminal status comment on that one PR only. Two sub-cases:
|
||||
#
|
||||
# 1. workflow_run trigger: the PR's native auto-index run fired
|
||||
# workflow_run, so github.event.workflow_run.id is the trigger.
|
||||
# Find the matching PR via the matrix entry whose runId matches.
|
||||
#
|
||||
# 2. workflow_dispatch trigger with inputs.pr_number set: the
|
||||
# index workflow's bot-fallback path dispatched us directly
|
||||
# because workflow_run is suppressed for GITHUB_TOKEN triggers.
|
||||
# Use inputs.pr_number as the comment target.
|
||||
#
|
||||
# Broadcast-commenting on every active PR would be noise — only the
|
||||
# PR that asked for a fresh index gets a reply.
|
||||
- name: Comment on PR — deploy complete
|
||||
if: always()
|
||||
uses: actions/github-script@v7
|
||||
env:
|
||||
MATRIX: ${{ steps.resolve.outputs.matrix }}
|
||||
TRIGGER_RUN_ID: ${{ github.event.workflow_run.id }}
|
||||
DISPATCH_PR_NUMBER: ${{ github.event.inputs.pr_number }}
|
||||
DEPLOY_STATUS: ${{ job.status }}
|
||||
with:
|
||||
script: |
|
||||
let prNum = null;
|
||||
|
||||
// Case 1: dispatched directly with pr_number (bot-fallback path)
|
||||
if (process.env.DISPATCH_PR_NUMBER && process.env.DISPATCH_PR_NUMBER !== '') {
|
||||
prNum = parseInt(process.env.DISPATCH_PR_NUMBER, 10);
|
||||
}
|
||||
// Case 2: workflow_run trigger from a PR index run
|
||||
else if (context.eventName === 'workflow_run') {
|
||||
const matrix = JSON.parse(process.env.MATRIX || '[]');
|
||||
const triggerRunId = Number(process.env.TRIGGER_RUN_ID);
|
||||
const match = matrix.find(
|
||||
(m) => m.runId === triggerRunId && m.name.startsWith('LibreChat-pr-'),
|
||||
);
|
||||
if (match) {
|
||||
prNum = parseInt(match.name.replace('LibreChat-pr-', ''), 10);
|
||||
}
|
||||
}
|
||||
|
||||
if (!prNum) {
|
||||
core.info('No PR to comment on (trigger was not a PR-scoped index); skipping.');
|
||||
return;
|
||||
}
|
||||
|
||||
const deployUrl = `${context.serverUrl}/${context.repo.owner}/${context.repo.repo}/actions/runs/${context.runId}`;
|
||||
const ok = process.env.DEPLOY_STATUS === 'success';
|
||||
const body = [
|
||||
`### GitNexus: ${ok ? '🚀 deployed' : '❌ deploy failed'}`,
|
||||
'',
|
||||
ok
|
||||
? `The \`LibreChat-pr-${prNum}\` index is now live on the MCP server.`
|
||||
: `The deploy failed — the previous index (if any) continues to be served.`,
|
||||
`[Deploy run](${deployUrl})`,
|
||||
].join('\n');
|
||||
await github.rest.issues.createComment({
|
||||
owner: context.repo.owner,
|
||||
repo: context.repo.repo,
|
||||
issue_number: prNum,
|
||||
body,
|
||||
});
|
||||
|
||||
- name: Cleanup SSH key
|
||||
if: always()
|
||||
run: rm -f ~/.ssh/deploy_key
|
||||
45
.github/workflows/gitnexus-index.yml
vendored
45
.github/workflows/gitnexus-index.yml
vendored
|
|
@ -31,7 +31,8 @@ on:
|
|||
|
||||
permissions:
|
||||
contents: read
|
||||
actions: write # needed to dispatch gitnexus-deploy-do.yml on bot-triggered runs
|
||||
actions: write # dispatch gitnexus-deploy.yml on bot-triggered runs
|
||||
pull-requests: write # post completion comments for /gitnexus command runs
|
||||
|
||||
concurrency:
|
||||
# When triggered by the /gitnexus command, group by PR number so rapid
|
||||
|
|
@ -168,10 +169,48 @@ jobs:
|
|||
uses: actions/github-script@v7
|
||||
with:
|
||||
script: |
|
||||
core.info('Triggering actor is github-actions[bot]; workflow_run would not fire. Dispatching gitnexus-deploy-do.yml manually.');
|
||||
core.info('Triggering actor is github-actions[bot]; workflow_run would not fire. Dispatching gitnexus-deploy.yml manually.');
|
||||
// Pass pr_number through so the deploy workflow knows which
|
||||
// PR to post its completion comment on (for /gitnexus
|
||||
// command runs this will be set; for other bot dispatches
|
||||
// it's empty and the deploy step falls back to matrix match).
|
||||
await github.rest.actions.createWorkflowDispatch({
|
||||
owner: context.repo.owner,
|
||||
repo: context.repo.repo,
|
||||
workflow_id: 'gitnexus-deploy-do.yml',
|
||||
workflow_id: 'gitnexus-deploy.yml',
|
||||
ref: 'main',
|
||||
inputs: {
|
||||
pr_number: '${{ inputs.pr_number }}',
|
||||
},
|
||||
});
|
||||
|
||||
# Reply on the PR when the /gitnexus command path runs so the
|
||||
# requester knows the index step finished. This only fires when
|
||||
# inputs.pr_number is set (command-triggered) AND the rest of the
|
||||
# job succeeded. A separate comment posts from the deploy workflow
|
||||
# when the live server has the fresh index.
|
||||
- name: Comment on PR — index complete
|
||||
if: always() && inputs.pr_number != ''
|
||||
uses: actions/github-script@v7
|
||||
with:
|
||||
script: |
|
||||
const outcome = '${{ job.status }}' === 'success' ? '✅ indexed' : '❌ index failed';
|
||||
const prNum = parseInt('${{ inputs.pr_number }}', 10);
|
||||
const runUrl = `${context.serverUrl}/${context.repo.owner}/${context.repo.repo}/actions/runs/${context.runId}`;
|
||||
const embeddingsFlag = '${{ inputs.embeddings }}' === 'true' ? 'with embeddings' : 'graph-only';
|
||||
const body = [
|
||||
`### GitNexus: ${outcome}`,
|
||||
``,
|
||||
`PR #${prNum} was indexed ${embeddingsFlag}.`,
|
||||
`[Index run](${runUrl})`,
|
||||
'',
|
||||
'${{ job.status }}' === 'success'
|
||||
? '⏳ Waiting for deploy to serve the fresh index…'
|
||||
: '_Index run failed — the previous index (if any) continues to be served._',
|
||||
].join('\n');
|
||||
await github.rest.issues.createComment({
|
||||
owner: context.repo.owner,
|
||||
repo: context.repo.repo,
|
||||
issue_number: prNum,
|
||||
body,
|
||||
});
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue