mirror of
https://github.com/danny-avila/LibreChat.git
synced 2026-08-03 22:32:42 +00:00
📊 experimental: Add GitNexus CI/CD and deployment configuration (#12577)
* feat: Add GitNexus CI/CD and deployment configuration - Introduced a Dockerfile for building the GitNexus application with necessary dependencies and configurations. - Added a Caddyfile to set up a reverse proxy with bearer token authentication for secure access to GitNexus. - Created an entrypoint script to validate the API token and start both GitNexus and Caddy services. - Configured Fly.io deployment settings in fly.toml, including health checks and service parameters. - Established GitHub Actions workflows for deploying the GitNexus index and managing deployments to Fly.io. * fix: use npx instead of bunx for native addon compatibility bunx skips node-gyp lifecycle scripts, so @ladybugdb/core's native .node binary never gets compiled/downloaded. npx handles this correctly.
This commit is contained in:
parent
96312aa4fd
commit
01a1bc1689
6 changed files with 283 additions and 0 deletions
21
.fly/gitnexus/Caddyfile
Normal file
21
.fly/gitnexus/Caddyfile
Normal file
|
|
@ -0,0 +1,21 @@
|
|||
:8080 {
|
||||
# Health check — unauthenticated so Fly.io can probe it
|
||||
@health path /health
|
||||
handle @health {
|
||||
reverse_proxy localhost:4747 {
|
||||
rewrite /api/info
|
||||
}
|
||||
}
|
||||
|
||||
# All other routes require bearer token
|
||||
@authed {
|
||||
header Authorization "Bearer {env.API_TOKEN}"
|
||||
}
|
||||
|
||||
handle @authed {
|
||||
reverse_proxy localhost:4747
|
||||
}
|
||||
|
||||
# Reject unauthenticated requests
|
||||
respond "Unauthorized" 401
|
||||
}
|
||||
29
.fly/gitnexus/Dockerfile
Normal file
29
.fly/gitnexus/Dockerfile
Normal file
|
|
@ -0,0 +1,29 @@
|
|||
FROM node:24-slim
|
||||
|
||||
ARG GITNEXUS_VERSION=1.5.3
|
||||
|
||||
# Build tools for native addons (LadybugDB, tree-sitter), cleaned up after install
|
||||
RUN apt-get update \
|
||||
&& apt-get install -y --no-install-recommends python3 make g++ caddy \
|
||||
&& npm install -g gitnexus@${GITNEXUS_VERSION} \
|
||||
&& apt-get purge -y --auto-remove python3 make g++ \
|
||||
&& rm -rf /var/lib/apt/lists/* /root/.npm
|
||||
|
||||
WORKDIR /repo
|
||||
|
||||
# Copy the pre-built GitNexus index (from CI artifact)
|
||||
COPY .gitnexus/ .gitnexus/
|
||||
|
||||
# Register the index so `gitnexus serve` can discover it
|
||||
RUN gitnexus index /repo --allow-non-git
|
||||
|
||||
# Caddy reverse proxy: bearer token auth in front of gitnexus serve
|
||||
# Token is set via FLY_API_SECRET (flyctl secrets set API_TOKEN=...)
|
||||
COPY Caddyfile /etc/caddy/Caddyfile
|
||||
|
||||
COPY entrypoint.sh /entrypoint.sh
|
||||
RUN chmod +x /entrypoint.sh
|
||||
|
||||
EXPOSE 8080
|
||||
|
||||
ENTRYPOINT ["/entrypoint.sh"]
|
||||
14
.fly/gitnexus/entrypoint.sh
Normal file
14
.fly/gitnexus/entrypoint.sh
Normal file
|
|
@ -0,0 +1,14 @@
|
|||
#!/bin/sh
|
||||
set -e
|
||||
|
||||
if [ -z "$API_TOKEN" ]; then
|
||||
echo "ERROR: API_TOKEN secret is not set."
|
||||
echo "Run: flyctl secrets set API_TOKEN=<your-token>"
|
||||
exit 1
|
||||
fi
|
||||
|
||||
# Start gitnexus serve in background
|
||||
gitnexus serve --host 127.0.0.1 --port 4747 &
|
||||
|
||||
# Start caddy auth proxy in foreground
|
||||
exec caddy run --config /etc/caddy/Caddyfile
|
||||
24
.fly/gitnexus/fly.toml
Normal file
24
.fly/gitnexus/fly.toml
Normal file
|
|
@ -0,0 +1,24 @@
|
|||
app = 'librechat-gitnexus'
|
||||
primary_region = 'iad'
|
||||
|
||||
[build]
|
||||
dockerfile = 'Dockerfile'
|
||||
|
||||
[http_service]
|
||||
internal_port = 8080
|
||||
force_https = true
|
||||
auto_stop_machines = 'stop'
|
||||
auto_start_machines = true
|
||||
min_machines_running = 1
|
||||
|
||||
[[vm]]
|
||||
size = 'shared-cpu-1x'
|
||||
memory = '512mb'
|
||||
|
||||
[checks]
|
||||
[checks.health]
|
||||
type = 'http'
|
||||
port = 8080
|
||||
path = '/health'
|
||||
interval = '30s'
|
||||
timeout = '5s'
|
||||
104
.github/workflows/gitnexus-deploy.yml
vendored
Normal file
104
.github/workflows/gitnexus-deploy.yml
vendored
Normal file
|
|
@ -0,0 +1,104 @@
|
|||
# Deploys the GitNexus index to Fly.io as a persistent MCP + REST server.
|
||||
#
|
||||
# Endpoints available after deploy:
|
||||
# /api/mcp — MCP-over-HTTP (StreamableHTTP transport)
|
||||
# /api/query — Search execution flows
|
||||
# /api/search — Hybrid BM25 + semantic search
|
||||
# /api/repos — List indexed repositories
|
||||
# /api/info — Server version and status
|
||||
#
|
||||
# First-time setup:
|
||||
# 1. flyctl apps create librechat-gitnexus
|
||||
# 2. flyctl tokens create deploy -x 999999h
|
||||
# 3. flyctl secrets set API_TOKEN=$(openssl rand -hex 32)
|
||||
# 4. Add FLY_API_TOKEN as a GitHub repo secret
|
||||
#
|
||||
# All requests (except /health) require: Authorization: Bearer <API_TOKEN>
|
||||
|
||||
name: GitNexus Deploy
|
||||
|
||||
on:
|
||||
workflow_run:
|
||||
workflows: ['GitNexus Index']
|
||||
branches: [main]
|
||||
types: [completed]
|
||||
workflow_dispatch:
|
||||
|
||||
permissions:
|
||||
actions: read
|
||||
|
||||
concurrency:
|
||||
group: gitnexus-deploy
|
||||
cancel-in-progress: true
|
||||
|
||||
env:
|
||||
GITNEXUS_VERSION: '1.5.3'
|
||||
|
||||
jobs:
|
||||
deploy:
|
||||
if: |
|
||||
github.event_name == 'workflow_dispatch' ||
|
||||
github.event.workflow_run.conclusion == 'success'
|
||||
runs-on: ubuntu-latest
|
||||
timeout-minutes: 10
|
||||
steps:
|
||||
- name: Checkout deploy config
|
||||
uses: actions/checkout@v4
|
||||
with:
|
||||
sparse-checkout: .fly/gitnexus
|
||||
fetch-depth: 1
|
||||
|
||||
- name: Resolve index run
|
||||
id: resolve
|
||||
uses: actions/github-script@v7
|
||||
with:
|
||||
script: |
|
||||
const runId = context.payload.workflow_run?.id;
|
||||
if (runId) {
|
||||
core.setOutput('run_id', String(runId));
|
||||
return;
|
||||
}
|
||||
// workflow_dispatch: find latest successful index run on main
|
||||
const { data } = await github.rest.actions.listWorkflowRuns({
|
||||
owner: context.repo.owner,
|
||||
repo: context.repo.repo,
|
||||
workflow_id: 'gitnexus-index.yml',
|
||||
branch: 'main',
|
||||
status: 'success',
|
||||
per_page: 1,
|
||||
});
|
||||
if (!data.workflow_runs.length) {
|
||||
core.setFailed('No successful GitNexus Index runs found on main');
|
||||
return;
|
||||
}
|
||||
core.setOutput('run_id', String(data.workflow_runs[0].id));
|
||||
|
||||
- name: Download GitNexus index
|
||||
uses: actions/download-artifact@v4
|
||||
with:
|
||||
name: gitnexus-index-main
|
||||
path: deploy/.gitnexus
|
||||
run-id: ${{ steps.resolve.outputs.run_id }}
|
||||
github-token: ${{ secrets.GITHUB_TOKEN }}
|
||||
|
||||
- name: Prepare deploy context
|
||||
run: |
|
||||
cp .fly/gitnexus/Dockerfile deploy/Dockerfile
|
||||
cp .fly/gitnexus/Caddyfile deploy/Caddyfile
|
||||
cp .fly/gitnexus/entrypoint.sh deploy/entrypoint.sh
|
||||
echo "Deploy context:"
|
||||
ls -la deploy/
|
||||
ls -la deploy/.gitnexus/
|
||||
|
||||
- name: Setup Fly
|
||||
uses: superfly/flyctl-actions/setup-flyctl@master
|
||||
|
||||
- name: Deploy to Fly.io
|
||||
working-directory: deploy
|
||||
run: |
|
||||
flyctl deploy \
|
||||
--config ../.fly/gitnexus/fly.toml \
|
||||
--build-arg GITNEXUS_VERSION=${{ env.GITNEXUS_VERSION }} \
|
||||
--remote-only
|
||||
env:
|
||||
FLY_API_TOKEN: ${{ secrets.FLY_API_TOKEN }}
|
||||
91
.github/workflows/gitnexus-index.yml
vendored
Normal file
91
.github/workflows/gitnexus-index.yml
vendored
Normal file
|
|
@ -0,0 +1,91 @@
|
|||
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
|
||||
|
||||
permissions:
|
||||
contents: read
|
||||
|
||||
concurrency:
|
||||
group: gitnexus-${{ github.ref }}
|
||||
cancel-in-progress: true
|
||||
|
||||
env:
|
||||
GITNEXUS_VERSION: '1.5.3'
|
||||
|
||||
jobs:
|
||||
index:
|
||||
# Allow push + dispatch unconditionally; filter PRs to contributors only
|
||||
if: |
|
||||
github.event_name != 'pull_request' ||
|
||||
github.event.pull_request.author_association == 'OWNER' ||
|
||||
github.event.pull_request.author_association == 'MEMBER' ||
|
||||
github.event.pull_request.author_association == 'COLLABORATOR'
|
||||
runs-on: ubuntu-latest
|
||||
timeout-minutes: 15
|
||||
steps:
|
||||
- name: Checkout repository
|
||||
uses: actions/checkout@v4
|
||||
with:
|
||||
fetch-depth: 1
|
||||
|
||||
- name: Setup Node.js
|
||||
uses: actions/setup-node@v4
|
||||
with:
|
||||
node-version: 24
|
||||
|
||||
- name: Cache npm store
|
||||
uses: actions/cache@v4
|
||||
with:
|
||||
path: ~/.npm
|
||||
key: gitnexus-npm-${{ runner.os }}-${{ env.GITNEXUS_VERSION }}
|
||||
restore-keys: gitnexus-npm-${{ runner.os }}-
|
||||
|
||||
- name: Run GitNexus Analyze
|
||||
run: |
|
||||
FLAGS="--skip-agents-md --verbose"
|
||||
if [ "${{ inputs.embeddings }}" = "true" ]; then
|
||||
FLAGS="$FLAGS --embeddings"
|
||||
fi
|
||||
if [ "${{ inputs.force }}" = "true" ]; then
|
||||
FLAGS="$FLAGS --force"
|
||||
fi
|
||||
npx --yes gitnexus@${{ env.GITNEXUS_VERSION }} analyze . $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:
|
||||
name: >-
|
||||
gitnexus-index-${{
|
||||
github.event_name == 'pull_request'
|
||||
&& format('pr-{0}', github.event.pull_request.number)
|
||||
|| github.ref_name
|
||||
}}
|
||||
path: .gitnexus/
|
||||
retention-days: 30
|
||||
Loading…
Add table
Add a link
Reference in a new issue