ci(llm-api): integrate into hub CI/CD pipeline

- Add llm-api to docker-build-push workflow (build, push, deploy)
- Add llm-api compose file to deploy-docker ALL_COMPOSE_FILES
- Add llm-api to update-submodule workflow
- Add llm-api CodeQL build step in security workflow
- Fix typo in Traefik host rule (asepharya -> asepharyana)
- Update CLAUDE.md submodule list
- Fix .gitmodules URL for llm-api submodule
- Clean up standalone infra files from llm-api submodule repo
This commit is contained in:
asepharyana
2026-07-25 13:39:35 +07:00
parent cbad784bca
commit e953c3cb67
8 changed files with 24 additions and 11 deletions
+1 -1
View File
@@ -89,7 +89,7 @@ jobs:
git fetch origin main --depth=1 || true git fetch origin main --depth=1 || true
# Detect changed files before resetting # Detect changed files before resetting
ALL_COMPOSE_FILES="infra/compose/traefik.yml infra/compose/shared.yml infra/compose/scraper.yml infra/compose/hub.yml infra/compose/tools.yml infra/compose/nats.yml infra/compose/dapr.yml infra/compose/observability.yml" ALL_COMPOSE_FILES="infra/compose/traefik.yml infra/compose/shared.yml infra/compose/scraper.yml infra/compose/hub.yml infra/compose/tools.yml infra/compose/llm-api.yml infra/compose/nats.yml infra/compose/dapr.yml infra/compose/observability.yml"
TRAEFIK_DYNAMIC_DIR="infra/traefik/dynamic" TRAEFIK_DYNAMIC_DIR="infra/traefik/dynamic"
if git rev-parse HEAD >/dev/null 2>&1; then if git rev-parse HEAD >/dev/null 2>&1; then
+11 -2
View File
@@ -8,6 +8,7 @@ on:
- 'apps/scraper/**' - 'apps/scraper/**'
- 'apps/hub/**' - 'apps/hub/**'
- 'apps/tools/**' - 'apps/tools/**'
- 'apps/llm-api/**'
- '.github/workflows/docker-build-push.yml' - '.github/workflows/docker-build-push.yml'
- 'infra/**' - 'infra/**'
- '!infra/compose/**' - '!infra/compose/**'
@@ -38,6 +39,7 @@ jobs:
scraper-api: ${{ steps.filter.outputs['scraper-api'] == 'true' || steps.dispatch.outputs['scraper-api'] == 'true' || github.event_name == 'workflow_dispatch' }} scraper-api: ${{ steps.filter.outputs['scraper-api'] == 'true' || steps.dispatch.outputs['scraper-api'] == 'true' || github.event_name == 'workflow_dispatch' }}
hub: ${{ steps.filter.outputs['hub'] == 'true' || steps.dispatch.outputs['hub'] == 'true' || github.event_name == 'workflow_dispatch' }} hub: ${{ steps.filter.outputs['hub'] == 'true' || steps.dispatch.outputs['hub'] == 'true' || github.event_name == 'workflow_dispatch' }}
tools: ${{ steps.filter.outputs['tools'] == 'true' || steps.dispatch.outputs['tools'] == 'true' || github.event_name == 'workflow_dispatch' }} tools: ${{ steps.filter.outputs['tools'] == 'true' || steps.dispatch.outputs['tools'] == 'true' || github.event_name == 'workflow_dispatch' }}
llm-api: ${{ steps.filter.outputs['llm-api'] == 'true' || steps.dispatch.outputs['llm-api'] == 'true' || github.event_name == 'workflow_dispatch' }}
steps: steps:
- uses: actions/checkout@v7 - uses: actions/checkout@v7
with: with:
@@ -66,6 +68,7 @@ jobs:
echo "scraper-api=$(changed '^(apps/scraper(/|$)|\.github/workflows/docker-build-push\.yml$|infra/docker/scraper\.Dockerfile$)')" >> "$GITHUB_OUTPUT" echo "scraper-api=$(changed '^(apps/scraper(/|$)|\.github/workflows/docker-build-push\.yml$|infra/docker/scraper\.Dockerfile$)')" >> "$GITHUB_OUTPUT"
echo "hub=$(changed '^(apps/hub(/|$)|\.github/workflows/docker-build-push\.yml$|infra/docker/hub\.Dockerfile$)')" >> "$GITHUB_OUTPUT" echo "hub=$(changed '^(apps/hub(/|$)|\.github/workflows/docker-build-push\.yml$|infra/docker/hub\.Dockerfile$)')" >> "$GITHUB_OUTPUT"
echo "tools=$(changed '^(apps/tools(/|$)|\.github/workflows/docker-build-push\.yml$|infra/docker/tools\.Dockerfile$)')" >> "$GITHUB_OUTPUT" echo "tools=$(changed '^(apps/tools(/|$)|\.github/workflows/docker-build-push\.yml$|infra/docker/tools\.Dockerfile$)')" >> "$GITHUB_OUTPUT"
echo "llm-api=$(changed '^(apps/llm-api(/|$)|\.github/workflows/docker-build-push\.yml$|infra/docker/llm-api\.Dockerfile$)')" >> "$GITHUB_OUTPUT"
- name: Parse repository_dispatch payload - name: Parse repository_dispatch payload
id: dispatch id: dispatch
@@ -87,7 +90,7 @@ jobs:
fi fi
case "$SERVICE" in case "$SERVICE" in
scraper-api|hub|tools) ;; scraper-api|hub|tools|llm-api) ;;
*) *)
exit 1 exit 1
;; ;;
@@ -95,7 +98,7 @@ jobs:
if ! [[ "$SHA" =~ ^[0-9a-fA-F]{40}$ ]]; then echo "::error::Invalid sha '$SHA'. Expected 40 hex characters"; fi if ! [[ "$SHA" =~ ^[0-9a-fA-F]{40}$ ]]; then echo "::error::Invalid sha '$SHA'. Expected 40 hex characters"; fi
SERVICES=(scraper-api hub tools) SERVICES=(scraper-api hub tools llm-api)
for svc in "${SERVICES[@]}"; do for svc in "${SERVICES[@]}"; do
if [ "$SERVICE" = "$svc" ]; then if [ "$SERVICE" = "$svc" ]; then
echo "${svc}=true" >> "$GITHUB_OUTPUT" echo "${svc}=true" >> "$GITHUB_OUTPUT"
@@ -115,6 +118,7 @@ jobs:
if [ "${{ steps.filter.outputs['scraper-api'] == 'true' || steps.dispatch.outputs['scraper-api'] == 'true' || github.event_name == 'workflow_dispatch' }}" == "true" ]; then add_service "scraper-api" "docker-scraper" "apps/scraper"; fi if [ "${{ steps.filter.outputs['scraper-api'] == 'true' || steps.dispatch.outputs['scraper-api'] == 'true' || github.event_name == 'workflow_dispatch' }}" == "true" ]; then add_service "scraper-api" "docker-scraper" "apps/scraper"; fi
if [ "${{ steps.filter.outputs['hub'] == 'true' || steps.dispatch.outputs['hub'] == 'true' || github.event_name == 'workflow_dispatch' }}" == "true" ]; then add_service "hub" "docker-hub" "apps/hub"; fi if [ "${{ steps.filter.outputs['hub'] == 'true' || steps.dispatch.outputs['hub'] == 'true' || github.event_name == 'workflow_dispatch' }}" == "true" ]; then add_service "hub" "docker-hub" "apps/hub"; fi
if [ "${{ steps.filter.outputs['tools'] == 'true' || steps.dispatch.outputs['tools'] == 'true' || github.event_name == 'workflow_dispatch' }}" == "true" ]; then add_service "tools" "docker-tools" "apps/tools"; fi if [ "${{ steps.filter.outputs['tools'] == 'true' || steps.dispatch.outputs['tools'] == 'true' || github.event_name == 'workflow_dispatch' }}" == "true" ]; then add_service "tools" "docker-tools" "apps/tools"; fi
if [ "${{ steps.filter.outputs['llm-api'] == 'true' || steps.dispatch.outputs['llm-api'] == 'true' || github.event_name == 'workflow_dispatch' }}" == "true" ]; then add_service "llm-api" "docker-llm-api" "apps/llm-api"; fi
JSON_ARRAY="[$(IFS=,; echo "${SERVICES[*]}")]" JSON_ARRAY="[$(IFS=,; echo "${SERVICES[*]}")]"
echo "matrix=$JSON_ARRAY" >> $GITHUB_OUTPUT echo "matrix=$JSON_ARRAY" >> $GITHUB_OUTPUT
@@ -136,6 +140,7 @@ jobs:
"scraper-api") REPO="https://github.com/asepharyana/asepharyana-hub-scraper.git" ;; "scraper-api") REPO="https://github.com/asepharyana/asepharyana-hub-scraper.git" ;;
"hub") REPO="https://github.com/asepharyana/asepharyana-hub-hub.git" ;; "hub") REPO="https://github.com/asepharyana/asepharyana-hub-hub.git" ;;
"tools") echo "tools is built from monorepo, no submodule wait needed"; exit 0 ;; "tools") echo "tools is built from monorepo, no submodule wait needed"; exit 0 ;;
"llm-api") REPO="https://github.com/asepharyana/asepharyana-hub-llm-api.git" ;;
*) *)
echo "::error::Unsupported service '$SERVICE'" echo "::error::Unsupported service '$SERVICE'"
exit 1 exit 1
@@ -224,6 +229,7 @@ jobs:
"scraper-api") echo "dockerfile=infra/docker/scraper.Dockerfile" >> $GITHUB_OUTPUT ;; "scraper-api") echo "dockerfile=infra/docker/scraper.Dockerfile" >> $GITHUB_OUTPUT ;;
"hub") echo "dockerfile=infra/docker/hub.Dockerfile" >> $GITHUB_OUTPUT ;; "hub") echo "dockerfile=infra/docker/hub.Dockerfile" >> $GITHUB_OUTPUT ;;
"tools") echo "dockerfile=infra/docker/tools.Dockerfile" >> $GITHUB_OUTPUT ;; "tools") echo "dockerfile=infra/docker/tools.Dockerfile" >> $GITHUB_OUTPUT ;;
"llm-api") echo "dockerfile=infra/docker/llm-api.Dockerfile" >> $GITHUB_OUTPUT ;;
esac esac
- name: Build and Push Docker image - name: Build and Push Docker image
@@ -270,11 +276,13 @@ jobs:
SERVICES["scraper-api"]="scraper.yml" SERVICES["scraper-api"]="scraper.yml"
SERVICES["hub"]="hub.yml" SERVICES["hub"]="hub.yml"
SERVICES["tools"]="tools.yml" SERVICES["tools"]="tools.yml"
SERVICES["llm-api"]="llm-api.yml"
declare -A PATHS declare -A PATHS
PATHS["scraper-api"]="apps/scraper" PATHS["scraper-api"]="apps/scraper"
PATHS["hub"]="apps/hub" PATHS["hub"]="apps/hub"
PATHS["tools"]="apps/tools" PATHS["tools"]="apps/tools"
PATHS["llm-api"]="apps/llm-api"
# Use git config for possible commits # Use git config for possible commits
git config --local user.email "action@github.com" git config --local user.email "action@github.com"
@@ -285,6 +293,7 @@ jobs:
if [ "${{ needs.changes.outputs['scraper-api'] }}" == "true" ] && [ "$id" == "scraper-api" ]; then SHOULD_HAVE_RUN=true; fi if [ "${{ needs.changes.outputs['scraper-api'] }}" == "true" ] && [ "$id" == "scraper-api" ]; then SHOULD_HAVE_RUN=true; fi
if [ "${{ needs.changes.outputs['hub'] }}" == "true" ] && [ "$id" == "hub" ]; then SHOULD_HAVE_RUN=true; fi if [ "${{ needs.changes.outputs['hub'] }}" == "true" ] && [ "$id" == "hub" ]; then SHOULD_HAVE_RUN=true; fi
if [ "${{ needs.changes.outputs['tools'] }}" == "true" ] && [ "$id" == "tools" ]; then SHOULD_HAVE_RUN=true; fi if [ "${{ needs.changes.outputs['tools'] }}" == "true" ] && [ "$id" == "tools" ]; then SHOULD_HAVE_RUN=true; fi
if [ "${{ needs.changes.outputs['llm-api'] }}" == "true" ] && [ "$id" == "llm-api" ]; then SHOULD_HAVE_RUN=true; fi
if [ "${{ github.event_name }}" == "workflow_dispatch" ]; then SHOULD_HAVE_RUN=true; fi if [ "${{ github.event_name }}" == "workflow_dispatch" ]; then SHOULD_HAVE_RUN=true; fi
+4 -3
View File
@@ -22,8 +22,9 @@ jobs:
with: with:
languages: rust languages: rust
- name: Build Rust project for CodeQL analysis - name: Build Rust projects for CodeQL analysis
run: cargo build run: |
working-directory: apps/scraper cargo build --manifest-path apps/scraper/Cargo.toml
cargo build --manifest-path apps/llm-api/Cargo.toml
- uses: github/codeql-action/analyze@v4 - uses: github/codeql-action/analyze@v4
+2 -1
View File
@@ -36,7 +36,7 @@ jobs:
fi fi
case "$SERVICE" in case "$SERVICE" in
scraper-api|hub) ;; scraper-api|hub|llm-api) ;;
*) *)
echo "::error::Unsupported service '$SERVICE'" echo "::error::Unsupported service '$SERVICE'"
exit 1 exit 1
@@ -55,6 +55,7 @@ jobs:
# Map service name to submodule path # Map service name to submodule path
case "$SERVICE" in case "$SERVICE" in
scraper-api) SUBMODULE_PATH="apps/scraper" ;; scraper-api) SUBMODULE_PATH="apps/scraper" ;;
llm-api) SUBMODULE_PATH="apps/llm-api" ;;
*) SUBMODULE_PATH="apps/${SERVICE}" ;; *) SUBMODULE_PATH="apps/${SERVICE}" ;;
esac esac
+1 -1
View File
@@ -12,4 +12,4 @@
url = https://github.com/asepharyana/asepharyana-hub-guide.git url = https://github.com/asepharyana/asepharyana-hub-guide.git
[submodule "apps/llm-api"] [submodule "apps/llm-api"]
path = apps/llm-api path = apps/llm-api
url = https://github.com/asepharyana/llm-api.git url = https://github.com/asepharyana/asepharyana-hub-llm-api.git
+3 -1
View File
@@ -26,7 +26,9 @@ asepharyana-hub/
- Submodule pointers are updated by CI/CD (via `repository_dispatch` or manual commit). - Submodule pointers are updated by CI/CD (via `repository_dispatch` or manual commit).
- Current submodules: - Current submodules:
- `apps/hub``asepharyana/asepharyana-hub-hub` - `apps/hub``asepharyana/asepharyana-hub-hub`
- `apps/scraper``asepharyana/asepharyana-hub-scraper`. - `apps/scraper``asepharyana/asepharyana-hub-scraper`
- `apps/llm-api``asepharyana/asepharyana-hub-llm-api`
- `apps/tools``asepharyana/asepharyana-hub-tools`.
### Infrastructure Stack ### Infrastructure Stack
- **Traefik v3.6** — reverse proxy, TLS termination, middleware chain, Prometheus metrics (`--metrics.prometheus=true`) - **Traefik v3.6** — reverse proxy, TLS termination, middleware chain, Prometheus metrics (`--metrics.prometheus=true`)
+1 -1
View File
@@ -22,7 +22,7 @@ http:
# ── LLM API (MiniCPM-V) ── # ── LLM API (MiniCPM-V) ──
llm-api: llm-api:
rule: 'Host(`ai.asepharyana.my.id`) || Host(`ai.asepharya.web.id`)' rule: 'Host(`ai.asepharyana.my.id`) || Host(`ai.asepharyana.web.id`)'
entryPoints: entryPoints:
- websecure - websecure
tls: {} tls: {}