diff --git a/.dockerignore b/.dockerignore index beb3c35d..6afcd294 100644 --- a/.dockerignore +++ b/.dockerignore @@ -3,4 +3,4 @@ !faceai/** !bin/ !bin/Face_Recognition_Unix/ -!bin/Face_Recognition_Unix/** \ No newline at end of file +!bin/Face_Recognition_Unix/face_matcher \ No newline at end of file diff --git a/.forgejo/workflows/publish-faceai-container.yml b/.forgejo/workflows/publish-faceai-container.yml index d665880f..356c5444 100644 --- a/.forgejo/workflows/publish-faceai-container.yml +++ b/.forgejo/workflows/publish-faceai-container.yml @@ -13,9 +13,11 @@ on: env: REGISTRY: ${{ vars.FORGEJO_REGISTRY }} IMAGE_NAMESPACE: ${{ vars.IMAGE_NAMESPACE }} - IMAGE_NAME: ${{ vars.IMAGE_NAME != '' && vars.IMAGE_NAME || 'faceai' }} + CLIENT_IMAGE_NAME: ${{ vars.IMAGE_NAME != '' && vars.IMAGE_NAME || 'faceai-client' }} + PROCESSOR_IMAGE_NAME: ${{ vars.PROCESSOR_IMAGE_NAME != '' && vars.PROCESSOR_IMAGE_NAME || 'faceai-processor' }} BUILD_CONTEXT: . - DOCKERFILE_PATH: faceai/docker/Dockerfile + CLIENT_DOCKERFILE_PATH: faceai/docker/Dockerfile + PROCESSOR_DOCKERFILE_PATH: faceai/docker/processor.Dockerfile jobs: publish: @@ -32,8 +34,10 @@ jobs: set -eu if [ -z "${REGISTRY}" ]; then echo "vars.FORGEJO_REGISTRY is required"; exit 1; fi if [ -z "${IMAGE_NAMESPACE}" ]; then echo "vars.IMAGE_NAMESPACE is required"; exit 1; fi - if [ -z "${IMAGE_NAME}" ]; then echo "vars.IMAGE_NAME resolved to an empty value"; exit 1; fi - if [ ! -f "${DOCKERFILE_PATH}" ]; then echo "Dockerfile not found at ${DOCKERFILE_PATH}"; exit 1; fi + if [ -z "${CLIENT_IMAGE_NAME}" ]; then echo "client image name resolved to an empty value"; exit 1; fi + if [ -z "${PROCESSOR_IMAGE_NAME}" ]; then echo "processor image name resolved to an empty value"; exit 1; fi + if [ ! -f "${CLIENT_DOCKERFILE_PATH}" ]; then echo "Dockerfile not found at ${CLIENT_DOCKERFILE_PATH}"; exit 1; fi + if [ ! -f "${PROCESSOR_DOCKERFILE_PATH}" ]; then echo "Dockerfile not found at ${PROCESSOR_DOCKERFILE_PATH}"; exit 1; fi if [ ! -f "faceai/package.json" ]; then echo "faceai/package.json is missing from the repository checkout"; exit 1; fi if [ ! -f "bin/Face_Recognition_Unix/face_matcher" ]; then echo "bin/Face_Recognition_Unix/face_matcher is missing from the repository checkout"; exit 1; fi @@ -106,14 +110,27 @@ jobs: run: | echo "${{ secrets.FORGEJO_REGISTRY_TOKEN }}" | docker login "${REGISTRY}" -u "${{ secrets.FORGEJO_REGISTRY_USERNAME }}" --password-stdin - - name: Build and push image + - name: Build and push client image run: | set -eu - IMAGE_REF="${REGISTRY}/${IMAGE_NAMESPACE}/${IMAGE_NAME}" + IMAGE_REF="${REGISTRY}/${IMAGE_NAMESPACE}/${CLIENT_IMAGE_NAME}" SHORT_SHA="$(echo "${GITHUB_SHA}" | cut -c1-12)" docker buildx build \ --builder forgejo-builder \ - --file "${DOCKERFILE_PATH}" \ + --file "${CLIENT_DOCKERFILE_PATH}" \ + --tag "${IMAGE_REF}:sha-${SHORT_SHA}" \ + --tag "${IMAGE_REF}:latest" \ + --push \ + "${BUILD_CONTEXT}" + + - name: Build and push processor image + run: | + set -eu + IMAGE_REF="${REGISTRY}/${IMAGE_NAMESPACE}/${PROCESSOR_IMAGE_NAME}" + SHORT_SHA="$(echo "${GITHUB_SHA}" | cut -c1-12)" + docker buildx build \ + --builder forgejo-builder \ + --file "${PROCESSOR_DOCKERFILE_PATH}" \ --tag "${IMAGE_REF}:sha-${SHORT_SHA}" \ --tag "${IMAGE_REF}:latest" \ --push \ diff --git a/faceai/README.md b/faceai/README.md index c0c6b32d..2e60a257 100644 --- a/faceai/README.md +++ b/faceai/README.md @@ -79,7 +79,7 @@ The local stack also mounts: - `./logs` into both the public FaceAI container and the processor container as the persistent diagnostics directory - `../www` into the PHP container so the real bridge files are used -The `processor` service is built from `docker/processor.Dockerfile` using the repository root as Docker build context. That image copies the checked-in `bin/Face_Recognition_Unix` directory into `/opt/face-recognition` during `docker build`, so the matcher binary is baked into the image instead of being mounted from the host at runtime. +The `processor` service is built from `docker/processor.Dockerfile` using the repository root as Docker build context. That image copies only the checked-in Unix `face_matcher` into the image, so the matcher is baked into the processor runtime without bringing along the other Unix or Windows binaries. ### Persistent Logs @@ -219,9 +219,9 @@ When enabled, the live suite also: If the processor logs show an error like `spawn /opt/face-recognition/face_matcher ENOENT`, the problem is not the upload flow itself. It means the running processor cannot see the matcher binary at the configured `FACEAI_MATCHER_BINARY` path. -With the current checked-in Dockerfiles, the matcher binary is copied into the image from the repository source tree during `docker build`. The runtime container no longer needs a host bind mount for `/opt/face-recognition`. +With the current checked-in Dockerfiles, only the Unix `face_matcher` is copied into the processor image from the repository source tree during `docker build`. The runtime container no longer needs a host bind mount for `/opt/face-recognition`. -Published images now get that binary because the Forgejo container workflow builds from the repository root, which lets `faceai/docker/Dockerfile` copy: +Published images now get that binary because the Forgejo container workflow builds a dedicated processor image from the repository root, which lets `faceai/docker/processor.Dockerfile` copy: ```text bin/Face_Recognition_Unix/face_matcher @@ -309,7 +309,7 @@ services: condition: service_healthy processor: - image: forgejo.maddoscientisto.net/maddo/faceai-client:latest + image: forgejo.maddoscientisto.net/maddo/faceai-processor:latest container_name: regalami-faceai-processor restart: unless-stopped command: @@ -384,7 +384,7 @@ Processor settings: | Variable | Required | Example | Purpose | | --- | --- | --- | --- | | `FACEAI_PKL_ROOT` | yes | `/data/pkl` | mounted race-to-PKL dataset root | -| `FACEAI_MATCHER_BINARY` | yes | `/opt/face-recognition/face_matcher` | matcher executable baked into the image | +| `FACEAI_MATCHER_BINARY` | yes | `/opt/face-recognition/face_matcher` | matcher executable baked into the processor image | | `FACEAI_WORKER_CONCURRENCY` | optional | `2` | BullMQ worker concurrency | | `FACEAI_WORKER_TIMEOUT_MS` | optional | `300000` | matcher timeout in milliseconds | diff --git a/faceai/apps/processor/src/worker-utils.js b/faceai/apps/processor/src/worker-utils.js index 2d53da62..e86c92b2 100644 --- a/faceai/apps/processor/src/worker-utils.js +++ b/faceai/apps/processor/src/worker-utils.js @@ -41,7 +41,7 @@ export async function runFaceMatcher({ matcherBinary, selfiePath, pklPath, csvPa child.on('error', (error) => { clearTimeout(timer); if (error?.code === 'ENOENT') { - reject(new Error(`face_matcher not found at ${matcherBinary}. Check FACEAI_MATCHER_BINARY and the processor bind mount.`)); + reject(new Error(`face_matcher not found at ${matcherBinary}. Check FACEAI_MATCHER_BINARY or the bundled Unix matcher binary in the processor image.`)); return; } reject(error); diff --git a/faceai/apps/processor/src/worker.js b/faceai/apps/processor/src/worker.js index e504c501..bf093f7d 100644 --- a/faceai/apps/processor/src/worker.js +++ b/faceai/apps/processor/src/worker.js @@ -21,7 +21,7 @@ async function ensureMatcherBinaryAvailable() { await fs.access(config.matcherBinary, fsConstants.X_OK); } catch (error) { console.error(`FaceAI processor cannot start because the matcher binary is unavailable: ${config.matcherBinary}`); - console.error('Ensure FACEAI_MATCHER_BINARY points to a real executable and that the processor bind mount contains face_matcher.'); + console.error('Ensure FACEAI_MATCHER_BINARY points to a real executable and that the processor image includes the Unix face_matcher binary.'); if (error?.code === 'ENOENT') { console.error('The configured matcher path does not exist inside the processor runtime.'); } else if (error?.code === 'EACCES') { diff --git a/faceai/docker/Dockerfile b/faceai/docker/Dockerfile index d0a07daa..b17aa1a5 100644 --- a/faceai/docker/Dockerfile +++ b/faceai/docker/Dockerfile @@ -15,9 +15,6 @@ COPY faceai/apps/processor/package.json apps/processor/package.json RUN npm install COPY faceai/ . -COPY bin/Face_Recognition_Unix /opt/face-recognition - -RUN chmod +x /opt/face-recognition/face_matcher RUN npm run build @@ -31,10 +28,8 @@ RUN apt-get update \ WORKDIR /app COPY --from=build /app /app -COPY --from=build /opt/face-recognition /opt/face-recognition ENV NODE_ENV=production -ENV FACEAI_MATCHER_BINARY=/opt/face-recognition/face_matcher EXPOSE 3001 CMD ["npm", "run", "start"] \ No newline at end of file diff --git a/faceai/docker/processor.Dockerfile b/faceai/docker/processor.Dockerfile index 572bbff4..82f846b8 100644 --- a/faceai/docker/processor.Dockerfile +++ b/faceai/docker/processor.Dockerfile @@ -8,7 +8,7 @@ RUN apt-get update \ WORKDIR /app COPY faceai /app -COPY bin/Face_Recognition_Unix /opt/face-recognition +COPY bin/Face_Recognition_Unix/face_matcher /opt/face-recognition/face_matcher RUN chmod +x /opt/face-recognition/face_matcher diff --git a/stacks/faceai.yml b/stacks/faceai.yml index 3e3ee0e3..7e5d8329 100644 --- a/stacks/faceai.yml +++ b/stacks/faceai.yml @@ -30,7 +30,7 @@ services: - redis processor: - image: forgejo.maddoscientisto.net/maddo/faceai-client:latest + image: forgejo.maddoscientisto.net/maddo/faceai-processor:latest container_name: regalami-faceai-processor restart: unless-stopped command: sh -c "mkdir -p /data/logs && npm run start:processor >> /data/logs/processor.log 2>&1"