From 4c9affed1c9ac9dc2a58180c35426d6c079a31bc Mon Sep 17 00:00:00 2001 From: Daniel Vejsada Date: Thu, 15 Oct 2026 09:01:49 +0200 Subject: [PATCH 1/2] ci: publish images manually, lite only; test against lite deps - images.yaml: drop the push-to-main trigger (workflow_dispatch only) and build only Dockerfile.lite. The full image is no longer published. - release.yaml: drop the tag-push trigger. It is now manual, with an optional `tag` input that defaults to the selected ref name. - docker-build-workflow.yaml and the legacy mirror jobs: lite image only. - ci.yml: install requirements.lite.txt instead of requirements.txt, so CI tests the dependency set we actually ship. Co-Authored-By: Claude Opus 5.5 --- .github/workflows/ci.yml | 5 +++-- .github/workflows/docker-build-workflow.yaml | 6 ++---- .github/workflows/images.yaml | 12 +++--------- .github/workflows/release.yaml | 11 +++++++---- 4 files changed, 15 insertions(+), 19 deletions(-) diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index 0c90dca8..67e785aa 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -22,7 +22,7 @@ jobs: uses: actions/cache@v4 with: path: ~/.cache/pip - key: ${{ runner.os }}-pip-${{ hashFiles('requirements.txt', 'test_requirements.txt') }} + key: ${{ runner.os }}-pip-${{ hashFiles('requirements.lite.txt', 'test_requirements.txt') }} restore-keys: | ${{ runner.os }}-pip- @@ -35,9 +35,10 @@ jobs: pandoc --version | head -1 - name: Install dependencies + # Test against the lite dependency set: it is the only image we ship. run: | python -m pip install --upgrade pip - pip install -r requirements.txt + pip install -r requirements.lite.txt pip install -r test_requirements.txt - name: Run unit tests diff --git a/.github/workflows/docker-build-workflow.yaml b/.github/workflows/docker-build-workflow.yaml index 5338994e..bb308570 100644 --- a/.github/workflows/docker-build-workflow.yaml +++ b/.github/workflows/docker-build-workflow.yaml @@ -14,12 +14,10 @@ jobs: permissions: contents: read packages: write + # Only the lite image is built; the full image (Dockerfile) is not published. strategy: matrix: include: - - target: main - file: Dockerfile - image_name: librechat-rag-api-dev - target: lite file: Dockerfile.lite image_name: librechat-rag-api-dev-lite @@ -86,7 +84,7 @@ jobs: strategy: fail-fast: false matrix: - image_name: [librechat-rag-api-dev, librechat-rag-api-dev-lite] + image_name: [librechat-rag-api-dev-lite] steps: - name: Copy the published tags diff --git a/.github/workflows/images.yaml b/.github/workflows/images.yaml index 8b04a503..3c8707ff 100644 --- a/.github/workflows/images.yaml +++ b/.github/workflows/images.yaml @@ -1,12 +1,9 @@ name: Docker Dev Images Build +# Manual only: images are published on demand, not on every push to main. +# Only the lite image is built; the full image (Dockerfile) is not published. on: workflow_dispatch: - push: - paths-ignore: - - 'README.md' - branches: - - main jobs: build: @@ -17,9 +14,6 @@ jobs: strategy: matrix: include: - - target: main - file: Dockerfile - image_name: librechat-rag-api-dev - target: lite file: Dockerfile.lite image_name: librechat-rag-api-dev-lite @@ -100,7 +94,7 @@ jobs: strategy: fail-fast: false matrix: - image_name: [librechat-rag-api-dev, librechat-rag-api-dev-lite] + image_name: [librechat-rag-api-dev-lite] steps: - name: Copy the published tags diff --git a/.github/workflows/release.yaml b/.github/workflows/release.yaml index 207f37ee..4c9b490d 100644 --- a/.github/workflows/release.yaml +++ b/.github/workflows/release.yaml @@ -1,10 +1,13 @@ name: Docker Dev Images Release +# Manual only: pushing a git tag no longer publishes images. on: workflow_dispatch: - push: - tags: - - '*' + inputs: + tag: + description: 'Image tag to publish (defaults to the selected branch or tag name)' + required: false + type: string permissions: contents: read @@ -17,4 +20,4 @@ jobs: # secrets unless they are passed. secrets: inherit with: - tag: ${{ github.ref_name }} + tag: ${{ inputs.tag || github.ref_name }} From 1a07399d1189ac00a79bd58e8c48c8cd7fff7670 Mon Sep 17 00:00:00 2001 From: Daniel Vejsada Date: Thu, 15 Oct 2026 09:26:56 +0200 Subject: [PATCH 2/2] ci: split into label-gated CI and a single manual CD workflow CI (ci.yml) runs tests only, and only on pull requests labelled "CI": on adding the label, and on later pushes or a reopen while it is set. It no longer runs on push to main. Other cleanups: - setup-python v4 -> v5 with built-in pip caching (drops the separate actions/cache step) and no pip self-upgrade. - OPENAI_API_KEY is set once at the job level. CD (cd.yml) replaces images.yaml, release.yaml and docker-build-workflow.yaml, which duplicated the same build. It is manual (workflow_dispatch) and publishes the lite image to ghcr.io//librechat-rag-api-dev-lite, tagged with the commit SHA plus a `tag` input (default `latest`). Removed with the old files: - the legacy-GHCR mirror jobs: upstream's tooling for its org move, never configured here (no LEGACY_GHCR_OWNER, no legacy-mirror env) - the "Free Disk Space" step, which was needed only for the full image - the redundant build `target` (Dockerfile.lite has a single stage) Also bumps docker/login-action v2 -> v3 and build-push-action v5 -> v6. actionlint passes. Co-Authored-By: Claude Opus 5.5 --- .github/workflows/cd.yml | 50 +++++++ .github/workflows/ci.yml | 51 ++++---- .github/workflows/docker-build-workflow.yaml | 119 ----------------- .github/workflows/images.yaml | 129 ------------------- .github/workflows/release.yaml | 23 ---- 5 files changed, 73 insertions(+), 299 deletions(-) create mode 100644 .github/workflows/cd.yml delete mode 100644 .github/workflows/docker-build-workflow.yaml delete mode 100644 .github/workflows/images.yaml delete mode 100644 .github/workflows/release.yaml diff --git a/.github/workflows/cd.yml b/.github/workflows/cd.yml new file mode 100644 index 00000000..271509a1 --- /dev/null +++ b/.github/workflows/cd.yml @@ -0,0 +1,50 @@ +name: CD + +# Builds and publishes the lite image to GHCR. Manual only: Actions → CD → Run workflow, +# pick the branch or tag to build. The image is always tagged with the commit SHA, plus +# the `tag` input (default `latest`; use e.g. `v1.2.0` for a release). +on: + workflow_dispatch: + inputs: + tag: + description: 'Image tag to publish in addition to the commit SHA' + required: true + default: latest + type: string + +permissions: + contents: read + packages: write + +jobs: + publish-lite: + runs-on: ubuntu-latest + + steps: + - uses: actions/checkout@v4 + + - uses: docker/setup-qemu-action@v3 + + - uses: docker/setup-buildx-action@v3 + + - uses: docker/login-action@v3 + with: + registry: ghcr.io + username: ${{ github.actor }} + password: ${{ secrets.GITHUB_TOKEN }} + + # Registry names must be lowercase, and the repository owner is not guaranteed to be. + - name: Resolve image name + run: printf 'GHCR_IMAGE=ghcr.io/%s/librechat-rag-api-dev-lite\n' "${GITHUB_REPOSITORY_OWNER,,}" >> "$GITHUB_ENV" + + - uses: docker/build-push-action@v6 + with: + context: . + file: Dockerfile.lite + push: true + tags: | + ${{ env.GHCR_IMAGE }}:${{ github.sha }} + ${{ env.GHCR_IMAGE }}:${{ inputs.tag }} + platforms: linux/amd64,linux/arm64 + cache-from: type=gha + cache-to: type=gha,mode=max diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index 67e785aa..ad47ff44 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -1,30 +1,34 @@ name: CI +# Tests only, and only on pull requests labelled "CI". Adding the label starts a +# run; later pushes to a labelled PR re-run it. Images are built by CD (cd.yml). on: - push: - branches: [ main ] pull_request: + types: [labeled, synchronize, reopened] + +permissions: + contents: read jobs: - build-and-test: + test: + # Skip runs triggered by adding some other label. + if: >- + contains(github.event.pull_request.labels.*.name, 'CI') && + (github.event.action != 'labeled' || github.event.label.name == 'CI') runs-on: ubuntu-latest + env: + OPENAI_API_KEY: test_key steps: - - name: Checkout repository - uses: actions/checkout@v4 + - uses: actions/checkout@v4 - - name: Set up Python 3.12 - uses: actions/setup-python@v4 + - uses: actions/setup-python@v5 with: python-version: '3.12' - - - name: Cache pip dependencies - uses: actions/cache@v4 - with: - path: ~/.cache/pip - key: ${{ runner.os }}-pip-${{ hashFiles('requirements.lite.txt', 'test_requirements.txt') }} - restore-keys: | - ${{ runner.os }}-pip- + cache: pip + cache-dependency-path: | + requirements.lite.txt + test_requirements.txt - name: Install pandoc # The .docx /text path converts through pandoc, and the end-to-end tests @@ -36,26 +40,17 @@ jobs: - name: Install dependencies # Test against the lite dependency set: it is the only image we ship. - run: | - python -m pip install --upgrade pip - pip install -r requirements.lite.txt - pip install -r test_requirements.txt + run: pip install -r requirements.lite.txt -r test_requirements.txt - name: Run unit tests - env: - OPENAI_API_KEY: test_key - run: | - pytest -ra --maxfail=1 --disable-warnings --junitxml=test-results-unit.xml + run: pytest -ra --maxfail=1 --disable-warnings --junitxml=test-results-unit.xml - name: Run integration tests (pgvector container) - env: - OPENAI_API_KEY: test_key - run: | - pytest -m integration --maxfail=3 --disable-warnings --junitxml=test-results-integration.xml + run: pytest -m integration --maxfail=3 --disable-warnings --junitxml=test-results-integration.xml - name: Upload test results if: always() uses: actions/upload-artifact@v4 with: name: test-results - path: test-results-*.xml \ No newline at end of file + path: test-results-*.xml diff --git a/.github/workflows/docker-build-workflow.yaml b/.github/workflows/docker-build-workflow.yaml deleted file mode 100644 index bb308570..00000000 --- a/.github/workflows/docker-build-workflow.yaml +++ /dev/null @@ -1,119 +0,0 @@ -name: Docker Dev Images Build - -on: - workflow_call: - inputs: - tag: - description: 'Tag for the Docker images' - required: true - type: string - -jobs: - build: - runs-on: ubuntu-latest - permissions: - contents: read - packages: write - # Only the lite image is built; the full image (Dockerfile) is not published. - strategy: - matrix: - include: - - target: lite - file: Dockerfile.lite - image_name: librechat-rag-api-dev-lite - - steps: - # Check out the repository - - name: Checkout - uses: actions/checkout@v4 - - # Set up QEMU - - name: Set up QEMU - uses: docker/setup-qemu-action@v3 - - # Set up Docker Buildx - - name: Set up Docker Buildx - uses: docker/setup-buildx-action@v3 - - # Log in to GitHub Container Registry - - name: Log in to GitHub Container Registry - uses: docker/login-action@v2 - with: - registry: ghcr.io - username: ${{ github.actor }} - password: ${{ secrets.GITHUB_TOKEN }} - - # Registry names must be lowercase, and the repository owner is not guaranteed to be. - - name: Resolve image name - env: - IMAGE_NAME: ${{ matrix.image_name }} - run: printf 'GHCR_IMAGE=ghcr.io/%s/%s\n' "${GITHUB_REPOSITORY_OWNER,,}" "$IMAGE_NAME" >> "$GITHUB_ENV" - - # Build and push Docker images for each target - - name: Build and push Docker images - uses: docker/build-push-action@v5 - with: - context: . - file: ${{ matrix.file }} - push: true - tags: | - ${{ env.GHCR_IMAGE }}:${{ github.sha }} - ${{ env.GHCR_IMAGE }}:${{ inputs.tag }} - platforms: linux/amd64,linux/arm64 - target: ${{ matrix.target }} - - # After the repository moved to another owner, deployments still pull the old GHCR - # namespace. Copying each published tag there keeps them updating until the deprecation - # window closes. Copies are cross-repo blob mounts within GHCR, so no image data is - # rebuilt or re-uploaded. - # - # It runs in its own job so the credential that can write to the legacy namespace lives - # in an environment, out of reach of workflows on other branches, and so a mirror failure - # cannot fail a publish. - # - # Set the LEGACY_GHCR_OWNER variable and the LEGACY_GHCR_TOKEN secret (a token with - # write:packages for that owner) to turn this on. - mirror: - name: Mirror ${{ matrix.image_name }} to the legacy namespace - if: ${{ vars.LEGACY_GHCR_OWNER != '' }} - needs: build - runs-on: ubuntu-latest - timeout-minutes: 15 - continue-on-error: true - environment: legacy-mirror - strategy: - fail-fast: false - matrix: - image_name: [librechat-rag-api-dev-lite] - - steps: - - name: Copy the published tags - env: - LEGACY_OWNER: ${{ vars.LEGACY_GHCR_OWNER }} - LEGACY_TOKEN: ${{ secrets.LEGACY_GHCR_TOKEN }} - IMAGE_NAME: ${{ matrix.image_name }} - TAGS: "${{ github.sha }} ${{ inputs.tag }}" - run: | - set -euo pipefail - - legacy_owner="${LEGACY_OWNER,,}" - if [ "$legacy_owner" = "${GITHUB_REPOSITORY_OWNER,,}" ]; then - echo "Legacy owner matches the current owner; nothing to mirror." - exit 0 - fi - if [ -z "$LEGACY_TOKEN" ]; then - echo "::warning::LEGACY_GHCR_OWNER is set but LEGACY_GHCR_TOKEN is not; skipping the mirror." - exit 0 - fi - - # Registry names must be lowercase, and the repository owner is not guaranteed to be. - source_image="ghcr.io/${GITHUB_REPOSITORY_OWNER,,}/${IMAGE_NAME}" - legacy_image="ghcr.io/${legacy_owner}/${IMAGE_NAME}" - - # This login reads both namespaces: it replaces the default ghcr.io credential. - echo "$LEGACY_TOKEN" | docker login ghcr.io -u "$legacy_owner" --password-stdin - - for tag in $TAGS; do - echo "Mirroring ${source_image}:${tag} -> ${legacy_image}:${tag}" - docker buildx imagetools create -t "${legacy_image}:${tag}" "${source_image}:${tag}" - done diff --git a/.github/workflows/images.yaml b/.github/workflows/images.yaml deleted file mode 100644 index 3c8707ff..00000000 --- a/.github/workflows/images.yaml +++ /dev/null @@ -1,129 +0,0 @@ -name: Docker Dev Images Build - -# Manual only: images are published on demand, not on every push to main. -# Only the lite image is built; the full image (Dockerfile) is not published. -on: - workflow_dispatch: - -jobs: - build: - runs-on: ubuntu-latest - permissions: - contents: read - packages: write - strategy: - matrix: - include: - - target: lite - file: Dockerfile.lite - image_name: librechat-rag-api-dev-lite - - steps: - # Free up disk space - - name: Free Disk Space - uses: jlumbroso/free-disk-space@main - with: - tool-cache: true - android: true - dotnet: true - haskell: true - large-packages: true - docker-images: true - swap-storage: true - - # Check out the repository - - name: Checkout - uses: actions/checkout@v4 - - # Set up QEMU - - name: Set up QEMU - uses: docker/setup-qemu-action@v3 - - # Set up Docker Buildx - - name: Set up Docker Buildx - uses: docker/setup-buildx-action@v3 - - # Log in to GitHub Container Registry - - name: Log in to GitHub Container Registry - uses: docker/login-action@v2 - with: - registry: ghcr.io - username: ${{ github.actor }} - password: ${{ secrets.GITHUB_TOKEN }} - - # Registry names must be lowercase, and the repository owner is not guaranteed to be. - - name: Resolve image name - env: - IMAGE_NAME: ${{ matrix.image_name }} - run: printf 'GHCR_IMAGE=ghcr.io/%s/%s\n' "${GITHUB_REPOSITORY_OWNER,,}" "$IMAGE_NAME" >> "$GITHUB_ENV" - - # Build and push Docker images for each target - - name: Build and push Docker images - uses: docker/build-push-action@v5 - with: - context: . - file: ${{ matrix.file }} - push: true - tags: | - ${{ env.GHCR_IMAGE }}:${{ github.sha }} - ${{ env.GHCR_IMAGE }}:latest - platforms: linux/amd64,linux/arm64 - target: ${{ matrix.target }} - cache-from: type=gha - cache-to: type=gha,mode=max - - # After the repository moved to another owner, deployments still pull the old GHCR - # namespace. Copying each published tag there keeps them updating until the deprecation - # window closes. Copies are cross-repo blob mounts within GHCR, so no image data is - # rebuilt or re-uploaded. - # - # It runs in its own job so the credential that can write to the legacy namespace lives - # in an environment, out of reach of workflows on other branches, and so a mirror failure - # cannot fail a publish. - # - # Set the LEGACY_GHCR_OWNER variable and the LEGACY_GHCR_TOKEN secret (a token with - # write:packages for that owner) to turn this on. - mirror: - name: Mirror ${{ matrix.image_name }} to the legacy namespace - if: ${{ vars.LEGACY_GHCR_OWNER != '' }} - needs: build - runs-on: ubuntu-latest - timeout-minutes: 15 - continue-on-error: true - environment: legacy-mirror - strategy: - fail-fast: false - matrix: - image_name: [librechat-rag-api-dev-lite] - - steps: - - name: Copy the published tags - env: - LEGACY_OWNER: ${{ vars.LEGACY_GHCR_OWNER }} - LEGACY_TOKEN: ${{ secrets.LEGACY_GHCR_TOKEN }} - IMAGE_NAME: ${{ matrix.image_name }} - TAGS: "${{ github.sha }} latest" - run: | - set -euo pipefail - - legacy_owner="${LEGACY_OWNER,,}" - if [ "$legacy_owner" = "${GITHUB_REPOSITORY_OWNER,,}" ]; then - echo "Legacy owner matches the current owner; nothing to mirror." - exit 0 - fi - if [ -z "$LEGACY_TOKEN" ]; then - echo "::warning::LEGACY_GHCR_OWNER is set but LEGACY_GHCR_TOKEN is not; skipping the mirror." - exit 0 - fi - - # Registry names must be lowercase, and the repository owner is not guaranteed to be. - source_image="ghcr.io/${GITHUB_REPOSITORY_OWNER,,}/${IMAGE_NAME}" - legacy_image="ghcr.io/${legacy_owner}/${IMAGE_NAME}" - - # This login reads both namespaces: it replaces the default ghcr.io credential. - echo "$LEGACY_TOKEN" | docker login ghcr.io -u "$legacy_owner" --password-stdin - - for tag in $TAGS; do - echo "Mirroring ${source_image}:${tag} -> ${legacy_image}:${tag}" - docker buildx imagetools create -t "${legacy_image}:${tag}" "${source_image}:${tag}" - done diff --git a/.github/workflows/release.yaml b/.github/workflows/release.yaml deleted file mode 100644 index 4c9b490d..00000000 --- a/.github/workflows/release.yaml +++ /dev/null @@ -1,23 +0,0 @@ -name: Docker Dev Images Release - -# Manual only: pushing a git tag no longer publishes images. -on: - workflow_dispatch: - inputs: - tag: - description: 'Image tag to publish (defaults to the selected branch or tag name)' - required: false - type: string - -permissions: - contents: read - packages: write - -jobs: - call-docker-build: - uses: ./.github/workflows/docker-build-workflow.yaml - # The mirror step reads LEGACY_GHCR_TOKEN, and a called workflow gets no - # secrets unless they are passed. - secrets: inherit - with: - tag: ${{ inputs.tag || github.ref_name }}