mirror of
https://github.com/danny-avila/LibreChat.git
synced 2026-05-13 07:46:47 +00:00
310 lines
13 KiB
YAML
310 lines
13 KiB
YAML
name: GitNexus Index
|
|
|
|
on:
|
|
push:
|
|
branches: [main, dev]
|
|
paths-ignore: ['**.md', 'docs/**', 'LICENSE', '.github/**']
|
|
pull_request:
|
|
branches: [main, dev]
|
|
paths-ignore: ['**.md', 'docs/**', 'LICENSE', '.github/**']
|
|
workflow_dispatch:
|
|
inputs:
|
|
embeddings:
|
|
description: 'Enable embedding generation (slow, increases index size)'
|
|
type: boolean
|
|
default: false
|
|
force:
|
|
description: 'Force full re-index'
|
|
type: boolean
|
|
default: false
|
|
# When invoked from the /gitnexus index PR command, the command
|
|
# workflow fills these so the index is built from the PR's head
|
|
# ref and uploaded under the PR-numbered artifact name.
|
|
pr_number:
|
|
description: 'PR number to index (set by /gitnexus command)'
|
|
type: string
|
|
default: ''
|
|
pr_ref:
|
|
description: 'Optional PR head ref to check out; defaults to refs/pull/<pr_number>/head when pr_number is set'
|
|
type: string
|
|
default: ''
|
|
|
|
permissions:
|
|
contents: read
|
|
|
|
concurrency:
|
|
# When triggered by the /gitnexus command, group by PR number so rapid
|
|
# re-runs coalesce. Otherwise group by git ref as before.
|
|
group: gitnexus-${{ inputs.pr_number != '' && format('pr-{0}', inputs.pr_number) || github.ref }}
|
|
cancel-in-progress: true
|
|
|
|
env:
|
|
GITNEXUS_VERSION: '1.5.3'
|
|
|
|
jobs:
|
|
index:
|
|
permissions:
|
|
contents: read
|
|
pull-requests: read # read changed files to decide whether embeddings are needed
|
|
# Push + dispatch run unconditionally. Native pull_request events
|
|
# are restricted to PRs authored by danny-avila only — this keeps
|
|
# automatic CI spend low on a repo with 200+ open PRs.
|
|
#
|
|
# Other contributors' PRs can still be indexed on demand:
|
|
# - /gitnexus index (PR comment command, contributor-gated)
|
|
# - workflow_dispatch (manual dispatch from Actions UI)
|
|
# Both bypass this filter because they arrive as workflow_dispatch,
|
|
# not pull_request.
|
|
if: |
|
|
github.event_name != 'pull_request' ||
|
|
github.event.pull_request.user.login == 'danny-avila'
|
|
runs-on: ubuntu-latest
|
|
timeout-minutes: 25
|
|
steps:
|
|
- name: Validate dispatch inputs
|
|
if: github.event_name == 'workflow_dispatch'
|
|
env:
|
|
PR_NUMBER: ${{ inputs.pr_number }}
|
|
PR_REF: ${{ inputs.pr_ref }}
|
|
run: |
|
|
set -euo pipefail
|
|
if [ -n "$PR_NUMBER" ]; then
|
|
if [[ ! "$PR_NUMBER" =~ ^[0-9]+$ ]]; then
|
|
echo "::error::pr_number must be numeric"
|
|
exit 1
|
|
fi
|
|
EXPECTED_REF="refs/pull/${PR_NUMBER}/head"
|
|
if [ -n "$PR_REF" ] && [ "$PR_REF" != "$EXPECTED_REF" ]; then
|
|
echo "::error::pr_ref must match ${EXPECTED_REF}"
|
|
exit 1
|
|
fi
|
|
elif [ -n "$PR_REF" ]; then
|
|
echo "::error::pr_ref requires pr_number"
|
|
exit 1
|
|
fi
|
|
|
|
- name: Resolve GitNexus flags
|
|
id: flags
|
|
env:
|
|
EVENT_NAME: ${{ github.event_name }}
|
|
ENABLE_EMBEDDINGS_INPUT: ${{ inputs.embeddings }}
|
|
GH_TOKEN: ${{ secrets.GITHUB_TOKEN }}
|
|
PR_NUM: ${{ github.event.pull_request.number }}
|
|
run: |
|
|
set -euo pipefail
|
|
|
|
# Decide whether to generate embeddings. Rules:
|
|
# push (main/dev) -> always embed
|
|
# pull_request -> embed ONLY when the PR changes files
|
|
# under paths that also trigger backend
|
|
# or frontend unit tests (api/, client/,
|
|
# packages/). Docs/config-only PRs skip
|
|
# embeddings to save ~3-5 min of CI.
|
|
# workflow_dispatch -> respect the explicit `embeddings` input
|
|
# (default false). This also covers the
|
|
# /gitnexus index [embeddings] command.
|
|
ENABLE_EMBEDDINGS=false
|
|
case "$EVENT_NAME" in
|
|
workflow_dispatch)
|
|
[ "$ENABLE_EMBEDDINGS_INPUT" = "true" ] && ENABLE_EMBEDDINGS=true
|
|
;;
|
|
push)
|
|
ENABLE_EMBEDDINGS=true
|
|
;;
|
|
pull_request)
|
|
CHANGED=$(gh api "repos/${{ github.repository }}/pulls/$PR_NUM/files" \
|
|
--paginate --jq '.[].filename' 2>/dev/null || echo "")
|
|
if printf '%s\n' "$CHANGED" | grep -qE '^(api/|client/|packages/)'; then
|
|
echo "PR #$PR_NUM touches unit-test paths (api|client|packages) — enabling embeddings"
|
|
ENABLE_EMBEDDINGS=true
|
|
else
|
|
echo "PR #$PR_NUM does not touch unit-test paths — graph-only index"
|
|
fi
|
|
;;
|
|
esac
|
|
|
|
if [ "$ENABLE_EMBEDDINGS" = "true" ]; then
|
|
echo "enable_embeddings=true" >> "$GITHUB_OUTPUT"
|
|
else
|
|
echo "enable_embeddings=false" >> "$GITHUB_OUTPUT"
|
|
fi
|
|
|
|
- name: Setup Node.js
|
|
uses: actions/setup-node@v4
|
|
with:
|
|
node-version: 24
|
|
|
|
- name: Install GitNexus CLI
|
|
working-directory: ${{ runner.temp }}
|
|
env:
|
|
NPM_CONFIG_AUDIT: false
|
|
NPM_CONFIG_CACHE: ${{ runner.temp }}/gitnexus-npm-cache
|
|
NPM_CONFIG_FUND: false
|
|
NPM_CONFIG_GLOBALCONFIG: ${{ runner.temp }}/gitnexus-cli/global-npmrc
|
|
NPM_CONFIG_REGISTRY: https://registry.npmjs.org/
|
|
NPM_CONFIG_USERCONFIG: ${{ runner.temp }}/gitnexus-cli/.npmrc
|
|
run: |
|
|
set -euo pipefail
|
|
mkdir -p "$RUNNER_TEMP/gitnexus-cli" "$RUNNER_TEMP/gitnexus-npm-cache"
|
|
: > "$RUNNER_TEMP/gitnexus-cli/global-npmrc"
|
|
printf '%s\n' \
|
|
'registry=https://registry.npmjs.org/' \
|
|
'audit=false' \
|
|
'fund=false' \
|
|
> "$RUNNER_TEMP/gitnexus-cli/.npmrc"
|
|
# Keep GitNexus' native DB dependency deterministic in fresh CI installs.
|
|
npm install \
|
|
--prefix "$RUNNER_TEMP/gitnexus-cli" \
|
|
--no-save \
|
|
--no-package-lock \
|
|
"gitnexus@${{ env.GITNEXUS_VERSION }}" \
|
|
"@ladybugdb/core@0.15.2"
|
|
test -x "$RUNNER_TEMP/gitnexus-cli/node_modules/.bin/gitnexus"
|
|
|
|
- name: Checkout repository
|
|
uses: actions/checkout@v4
|
|
with:
|
|
# When the /gitnexus command dispatches us with a pr_ref, it's
|
|
# a refs/pull/<N>/head ref that GitHub mirrors into the base
|
|
# repo for every PR, so checkout works for fork PRs too. When
|
|
# pr_ref is empty (native push/pull_request), fall back to the
|
|
# default ref actions/checkout would use.
|
|
ref: ${{ inputs.pr_ref || (inputs.pr_number != '' && format('refs/pull/{0}/head', inputs.pr_number) || '') }}
|
|
fetch-depth: 1
|
|
persist-credentials: false
|
|
|
|
- name: Run GitNexus Analyze
|
|
working-directory: ${{ runner.temp }}
|
|
env:
|
|
ENABLE_EMBEDDINGS: ${{ steps.flags.outputs.enable_embeddings }}
|
|
FORCE: ${{ inputs.force }}
|
|
GITNEXUS_BIN: ${{ runner.temp }}/gitnexus-cli/node_modules/.bin/gitnexus
|
|
NPM_CONFIG_AUDIT: false
|
|
NPM_CONFIG_CACHE: ${{ runner.temp }}/gitnexus-npm-cache
|
|
NPM_CONFIG_FUND: false
|
|
NPM_CONFIG_GLOBALCONFIG: ${{ runner.temp }}/gitnexus-cli/global-npmrc
|
|
NPM_CONFIG_REGISTRY: https://registry.npmjs.org/
|
|
NPM_CONFIG_USERCONFIG: ${{ runner.temp }}/gitnexus-cli/.npmrc
|
|
run: |
|
|
set -euo pipefail
|
|
FLAGS=(--skip-agents-md --verbose)
|
|
|
|
if [ "$ENABLE_EMBEDDINGS" = "true" ]; then
|
|
FLAGS+=(--embeddings)
|
|
fi
|
|
if [ "$FORCE" = "true" ]; then
|
|
FLAGS+=(--force)
|
|
fi
|
|
"$GITNEXUS_BIN" analyze "$GITHUB_WORKSPACE" "${FLAGS[@]}"
|
|
|
|
- name: Verify index
|
|
run: |
|
|
if [ ! -d ".gitnexus" ] || [ ! -f ".gitnexus/meta.json" ]; then
|
|
echo "::error::GitNexus index was not created"
|
|
exit 1
|
|
fi
|
|
echo "::group::Index metadata"
|
|
cat .gitnexus/meta.json
|
|
echo ""
|
|
echo "::endgroup::"
|
|
|
|
- name: Upload GitNexus index
|
|
uses: actions/upload-artifact@v4
|
|
with:
|
|
# Artifact naming order of precedence:
|
|
# 1. /gitnexus command dispatch: inputs.pr_number -> pr-<N>
|
|
# 2. Native pull_request event: github.event.pull_request.number
|
|
# 3. Push or manual dispatch without pr_number: github.ref_name
|
|
name: >-
|
|
gitnexus-index-${{
|
|
inputs.pr_number != ''
|
|
&& format('pr-{0}', inputs.pr_number)
|
|
|| (github.event_name == 'pull_request'
|
|
&& format('pr-{0}', github.event.pull_request.number)
|
|
|| github.ref_name)
|
|
}}
|
|
path: .gitnexus/
|
|
include-hidden-files: true
|
|
retention-days: 30
|
|
|
|
post-index:
|
|
needs: index
|
|
if: |
|
|
always() &&
|
|
(inputs.pr_number != '' ||
|
|
(github.triggering_actor == 'github-actions[bot]' && needs.index.result == 'success'))
|
|
runs-on: ubuntu-latest
|
|
timeout-minutes: 5
|
|
permissions:
|
|
contents: read
|
|
actions: write # dispatch gitnexus-deploy.yml on bot-triggered runs
|
|
pull-requests: write # post completion comments for /gitnexus command runs
|
|
steps:
|
|
# GitHub suppresses workflow_run events for workflow runs whose
|
|
# triggering actor is GITHUB_TOKEN (to prevent recursive chaining).
|
|
# That means when this workflow is dispatched by gitnexus-pr-command
|
|
# via `gh api workflow_dispatch`, the deploy workflow's workflow_run
|
|
# trigger never fires. Manually dispatch the deploy here in that
|
|
# specific case — user-triggered runs continue to rely on the
|
|
# existing workflow_run trigger, so we don't double-deploy.
|
|
- name: Trigger deploy workflow for bot-triggered runs
|
|
if: github.triggering_actor == 'github-actions[bot]' && needs.index.result == 'success'
|
|
uses: actions/github-script@v7
|
|
env:
|
|
PR_NUMBER: ${{ inputs.pr_number }}
|
|
with:
|
|
script: |
|
|
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.yml',
|
|
ref: 'main',
|
|
inputs: {
|
|
pr_number: process.env.PR_NUMBER || '',
|
|
},
|
|
});
|
|
|
|
# Reply on the PR when the /gitnexus command path runs so the
|
|
# requester knows the index step finished. This fires when
|
|
# inputs.pr_number is set and reports the index job result. A
|
|
# separate comment posts from the deploy workflow when the live
|
|
# server has the fresh index.
|
|
- name: Comment on PR — index complete
|
|
if: inputs.pr_number != ''
|
|
uses: actions/github-script@v7
|
|
env:
|
|
EMBEDDINGS_INPUT: ${{ inputs.embeddings }}
|
|
INDEX_RESULT: ${{ needs.index.result }}
|
|
PR_NUMBER: ${{ inputs.pr_number }}
|
|
with:
|
|
script: |
|
|
const indexSucceeded = process.env.INDEX_RESULT === 'success';
|
|
const outcome = indexSucceeded ? '✅ indexed' : '❌ index failed';
|
|
const prNum = parseInt(process.env.PR_NUMBER || '', 10);
|
|
if (!Number.isSafeInteger(prNum)) {
|
|
core.setFailed(`Invalid PR number: ${process.env.PR_NUMBER}`);
|
|
return;
|
|
}
|
|
const runUrl = `${context.serverUrl}/${context.repo.owner}/${context.repo.repo}/actions/runs/${context.runId}`;
|
|
const embeddingsFlag = process.env.EMBEDDINGS_INPUT === 'true' ? 'with embeddings' : 'graph-only';
|
|
const body = [
|
|
`### GitNexus: ${outcome}`,
|
|
``,
|
|
`PR #${prNum} was indexed ${embeddingsFlag}.`,
|
|
`[Index run](${runUrl})`,
|
|
'',
|
|
indexSucceeded
|
|
? '⏳ 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,
|
|
});
|