Skip to content

[FIX] Remove VERSION arg from Dockerfile base stages to fix CI cache#1864

Open
chandrasekharan-zipstack wants to merge 4 commits intomainfrom
fix/docker-cache-version-label
Open

[FIX] Remove VERSION arg from Dockerfile base stages to fix CI cache#1864
chandrasekharan-zipstack wants to merge 4 commits intomainfrom
fix/docker-cache-version-label

Conversation

@chandrasekharan-zipstack
Copy link
Contributor

@chandrasekharan-zipstack chandrasekharan-zipstack commented Mar 18, 2026

What

Remove ARG VERSION and LABEL version="${VERSION}" from the base stage of all 7 Python Dockerfiles.

Why

The ARG VERSION used in LABEL version="${VERSION}" made every Docker layer's cache key depend on the version tag. Since the tag changes every build (rc.245, rc.246, ...), BuildKit could never match cached layers — 0% cache hit rate for all Python services in CI.

The frontend (55% cached) was unaffected because its Dockerfile has no ARG VERSION in any build stage.

Evidence from build run #23239022262:

  • Backend log (job 67550562986): cache manifest loads successfully but zero layers match
  • Frontend log (job 67550562697): cache manifest loads and multiple layers hit CACHED

How

Removed ARG VERSION=dev and the version="${VERSION}" from the LABEL instruction in the base stage of all Python Dockerfiles. The version label was unused — nothing in Helm charts, CI, or Kubernetes reads it. The image tag itself (e.g., backend:rc.246) carries the version.

Affected: backend, prompt-service, platform-service, runner, x2text-service, worker-unified, tool-sidecar.

Can this PR break any existing features. If yes, please list possible items. If no, please explain why. (PS: Admins do not merge the PR without this section filled)

No. The version label was purely decorative metadata on the Docker image. No code, Helm chart, or deployment reads it. The image tag already carries the version. Local test confirmed: building with VERSION=cache-test-1 then VERSION=cache-test-2 produces 100% cache hit (0.0s build).

Database Migrations

  • None

Env Config

  • None

Relevant Docs

Related Issues or PRs

  • Zipstack/unstract-cloud#1375 (cloud repo: Nuitka stage isolation + cloud patch cleanup)

Dependencies Versions

  • None

Notes on Testing

  • First build with this change populates cache
  • Second build with a different tag should show >0% cached for all Python services
  • Verified locally: two builds with different VERSION args, second build is 100% CACHED

Screenshots

N/A

Checklist

I have read and understood the Contribution Guidelines.

The ARG VERSION + LABEL version="${VERSION}" in the base stage caused
every Docker layer's cache key to depend on the version tag. Since the
tag changes every build (rc.245, rc.246, ...), BuildKit could never
match cached layers — resulting in 0% cache hit rate for all Python
services in CI.

The frontend (55% cached) was unaffected because its Dockerfile has no
ARG VERSION in any stage.

Removed from: backend, prompt-service, platform-service, runner,
x2text-service, worker-unified, tool-sidecar.

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
@coderabbitai
Copy link
Contributor

coderabbitai bot commented Mar 18, 2026

Summary by CodeRabbit

  • Chores
    • Updated Docker container version metadata across all service images. Version information is now exposed through the UNSTRACT_APPS_VERSION environment variable at runtime rather than in image labels.

Walkthrough

Build version handling is refactored across seven Dockerfiles by removing the top-level ARG VERSION=dev declaration and the version label, then relocating both to the final stage and exposing the version through the UNSTRACT_APPS_VERSION environment variable instead of image metadata.

Changes

Cohort / File(s) Summary
Version propagation refactoring
docker/dockerfiles/backend.Dockerfile, docker/dockerfiles/platform.Dockerfile, docker/dockerfiles/prompt.Dockerfile, docker/dockerfiles/runner.Dockerfile, docker/dockerfiles/tool-sidecar.Dockerfile, docker/dockerfiles/worker-unified.Dockerfile, docker/dockerfiles/x2text.Dockerfile
Removed ARG VERSION=dev from the top of each Dockerfile and removed version="${VERSION}" from the LABEL block. Reintroduced ARG VERSION=dev in the final stage and added ENV UNSTRACT_APPS_VERSION=${VERSION} to expose the build version as a runtime environment variable instead of an image label, optimizing layer caching.

Estimated code review effort

🎯 2 (Simple) | ⏱️ ~10 minutes

🚥 Pre-merge checks | ✅ 2 | ❌ 1

❌ Failed checks (1 warning)

Check name Status Explanation Resolution
Docstring Coverage ⚠️ Warning Docstring coverage is 50.00% which is insufficient. The required threshold is 80.00%. Write docstrings for the functions missing them to satisfy the coverage threshold.
✅ Passed checks (2 passed)
Check name Status Explanation
Title check ✅ Passed The title accurately summarizes the main change: removing VERSION arg from Dockerfile base stages to fix CI cache issues.
Description check ✅ Passed The description comprehensively covers all template sections with clear explanations of what changed, why (0% cache hit due to version arg), how it was fixed, and confirmation that no existing features break.

✏️ Tip: You can configure your own custom pre-merge checks in the settings.

✨ Finishing Touches
🧪 Generate unit tests (beta)
  • Create PR with unit tests
  • Commit unit tests in branch fix/docker-cache-version-label

Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out.

❤️ Share

Comment @coderabbitai help to get the list of available commands and usage tips.

@greptile-apps
Copy link
Contributor

greptile-apps bot commented Mar 18, 2026

Greptile Summary

This PR fixes a 0% Docker layer cache hit rate for all 7 Python services in CI by removing ARG VERSION from the base stage of each Dockerfile. Because ARG values participate in BuildKit's cache key computation, passing a new VERSION build argument (e.g. rc.245rc.246) on every CI run invalidated every downstream layer — including the expensive dependency installation stages — resulting in a full rebuild each time.

Key changes:

  • ARG VERSION=dev and LABEL version="${VERSION}" removed from the base stage in all 7 Dockerfiles (backend, platform, prompt, runner, tool-sidecar, worker-unified, x2text).
  • ARG VERSION=dev + ENV UNSTRACT_APPS_VERSION=${VERSION} added at the very end of each production stage (after all dependency installation), so only one thin metadata layer is rebuilt per version bump.
  • The LABEL retains maintainer and description but drops version; the image tag itself (service:rc.246) continues to carry version identity.

Effect on build graph: The base and ext-dependencies stages are now version-agnostic, meaning their layers are reusable across every CI build as long as system packages and Python dependencies haven't changed — matching the behaviour already seen for the frontend.

Confidence Score: 4/5

  • Safe to merge — the change is purely a Docker build metadata adjustment with no impact on runtime behaviour, Helm charts, or Kubernetes deployments.
  • The root-cause analysis is correct and the fix is applied consistently across all 7 affected Dockerfiles. Moving ARG VERSION to the final layer eliminates the cache invalidation cascade while preserving all expensive dependency layers. No application code is touched. The only open question (the unused UNSTRACT_APPS_VERSION env var and whether LABEL would be cleaner) has already been raised in prior review threads and is a minor concern that doesn't block the core cache fix from working.
  • No files require special attention; all 7 Dockerfiles follow the same corrected pattern.

Important Files Changed

Filename Overview
docker/dockerfiles/backend.Dockerfile Removed ARG VERSION / LABEL version from base stage; moved ARG VERSION=dev + ENV UNSTRACT_APPS_VERSION to the very end of the production stage. Cache-fix is correct; the comment explaining the intent is only present here and in worker-unified.Dockerfile but not the other five files.
docker/dockerfiles/platform.Dockerfile Same pattern as backend — ARG VERSION removed from base, ARG VERSION=dev + ENV UNSTRACT_APPS_VERSION added at end of production stage. No explanatory comment added.
docker/dockerfiles/prompt.Dockerfile Same pattern applied consistently. ARG/ENV placed correctly after all dependency installation steps and before CMD.
docker/dockerfiles/runner.Dockerfile Same pattern applied consistently. ARG/ENV placed after EXPOSE 5002 and before CMD.
docker/dockerfiles/tool-sidecar.Dockerfile Same pattern applied. No EXPOSE instruction in this Dockerfile (expected for a sidecar); ARG/ENV placed after the final RUN and before CMD.
docker/dockerfiles/worker-unified.Dockerfile ARG VERSION=dev / ENV UNSTRACT_APPS_VERSION placed after USER worker — semantically valid since ARG/ENV are build-time directives unaffected by the active user. Explanatory comment is present.
docker/dockerfiles/x2text.Dockerfile Same pattern applied consistently. ARG/ENV placed after EXPOSE 3004 and before CMD.

Flowchart

%%{init: {'theme': 'neutral'}}%%
flowchart TD
    subgraph BEFORE["BEFORE (cache broken)"]
        A1["base stage\nARG VERSION=rc.245\nLABEL version='rc.245'"] -->|"VERSION changes → cache miss"| B1["ext-dependencies stage\n(rebuilt every time)"]
        B1 --> C1["production stage\n(rebuilt every time)"]
        C1 --> D1["ENTRYPOINT / CMD"]
    end

    subgraph AFTER["AFTER (cache fixed)"]
        A2["base stage\nLABEL maintainer + description\n(no VERSION arg)"] -->|"stable → cache hit ✅"| B2["ext-dependencies stage\n(cached ✅)"]
        B2 -->|"deps unchanged → cache hit ✅"| C2["production stage\n(app code layer)"]
        C2 --> E2["ARG VERSION=rc.246\nENV UNSTRACT_APPS_VERSION\n(thin layer, rebuilt)"]
        E2 --> D2["ENTRYPOINT / CMD"]
    end

    style BEFORE fill:#ffe0e0,stroke:#cc0000
    style AFTER fill:#e0ffe0,stroke:#00aa00
    style A1 fill:#ffcccc
    style B1 fill:#ffcccc
    style C1 fill:#ffcccc
    style A2 fill:#ccffcc
    style B2 fill:#ccffcc
    style C2 fill:#ccffcc
    style E2 fill:#fffacc
Loading

Reviews (4): Last reviewed commit: "Revert "[MISC] Trivial log message clean..." | Re-trigger Greptile

@coderabbitai
Copy link
Contributor

coderabbitai bot commented Mar 18, 2026

Note

Unit test generation is a public access feature. Expect some limitations and changes as we gather feedback and continue to improve it.


Generating unit tests... This may take up to 20 minutes.

@coderabbitai
Copy link
Contributor

coderabbitai bot commented Mar 18, 2026

✅ Created PR with unit tests: #1865

Fix double period in backend health check log and remove ellipsis
from prompt-service init log. Used to test Docker layer cache reuse
after app code changes (ext-dependencies and nuitka-compile stages
should remain cached).

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
Add ARG VERSION + ENV UNSTRACT_APPS_VERSION at the end of the
production stage in all Python Dockerfiles. This makes the build
version available at runtime (os.environ["UNSTRACT_APPS_VERSION"])
without affecting layer caching — ARG is scoped to the production
stage and placed after all expensive build steps.

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
@github-actions
Copy link
Contributor

Test Results

Summary
  • Runner Tests: 11 passed, 0 failed (11 total)
  • SDK1 Tests: 63 passed, 0 failed (63 total)

Runner Tests - Full Report
filepath function $$\textcolor{#23d18b}{\tt{passed}}$$ SUBTOTAL
$$\textcolor{#23d18b}{\tt{runner/src/unstract/runner/clients/test\_docker.py}}$$ $$\textcolor{#23d18b}{\tt{test\_logs}}$$ $$\textcolor{#23d18b}{\tt{1}}$$ $$\textcolor{#23d18b}{\tt{1}}$$
$$\textcolor{#23d18b}{\tt{runner/src/unstract/runner/clients/test\_docker.py}}$$ $$\textcolor{#23d18b}{\tt{test\_cleanup}}$$ $$\textcolor{#23d18b}{\tt{1}}$$ $$\textcolor{#23d18b}{\tt{1}}$$
$$\textcolor{#23d18b}{\tt{runner/src/unstract/runner/clients/test\_docker.py}}$$ $$\textcolor{#23d18b}{\tt{test\_cleanup\_skip}}$$ $$\textcolor{#23d18b}{\tt{1}}$$ $$\textcolor{#23d18b}{\tt{1}}$$
$$\textcolor{#23d18b}{\tt{runner/src/unstract/runner/clients/test\_docker.py}}$$ $$\textcolor{#23d18b}{\tt{test\_client\_init}}$$ $$\textcolor{#23d18b}{\tt{1}}$$ $$\textcolor{#23d18b}{\tt{1}}$$
$$\textcolor{#23d18b}{\tt{runner/src/unstract/runner/clients/test\_docker.py}}$$ $$\textcolor{#23d18b}{\tt{test\_get\_image\_exists}}$$ $$\textcolor{#23d18b}{\tt{1}}$$ $$\textcolor{#23d18b}{\tt{1}}$$
$$\textcolor{#23d18b}{\tt{runner/src/unstract/runner/clients/test\_docker.py}}$$ $$\textcolor{#23d18b}{\tt{test\_get\_image}}$$ $$\textcolor{#23d18b}{\tt{1}}$$ $$\textcolor{#23d18b}{\tt{1}}$$
$$\textcolor{#23d18b}{\tt{runner/src/unstract/runner/clients/test\_docker.py}}$$ $$\textcolor{#23d18b}{\tt{test\_get\_container\_run\_config}}$$ $$\textcolor{#23d18b}{\tt{1}}$$ $$\textcolor{#23d18b}{\tt{1}}$$
$$\textcolor{#23d18b}{\tt{runner/src/unstract/runner/clients/test\_docker.py}}$$ $$\textcolor{#23d18b}{\tt{test\_get\_container\_run\_config\_without\_mount}}$$ $$\textcolor{#23d18b}{\tt{1}}$$ $$\textcolor{#23d18b}{\tt{1}}$$
$$\textcolor{#23d18b}{\tt{runner/src/unstract/runner/clients/test\_docker.py}}$$ $$\textcolor{#23d18b}{\tt{test\_run\_container}}$$ $$\textcolor{#23d18b}{\tt{1}}$$ $$\textcolor{#23d18b}{\tt{1}}$$
$$\textcolor{#23d18b}{\tt{runner/src/unstract/runner/clients/test\_docker.py}}$$ $$\textcolor{#23d18b}{\tt{test\_get\_image\_for\_sidecar}}$$ $$\textcolor{#23d18b}{\tt{1}}$$ $$\textcolor{#23d18b}{\tt{1}}$$
$$\textcolor{#23d18b}{\tt{runner/src/unstract/runner/clients/test\_docker.py}}$$ $$\textcolor{#23d18b}{\tt{test\_sidecar\_container}}$$ $$\textcolor{#23d18b}{\tt{1}}$$ $$\textcolor{#23d18b}{\tt{1}}$$
$$\textcolor{#23d18b}{\tt{TOTAL}}$$ $$\textcolor{#23d18b}{\tt{11}}$$ $$\textcolor{#23d18b}{\tt{11}}$$
SDK1 Tests - Full Report
filepath function $$\textcolor{#23d18b}{\tt{passed}}$$ SUBTOTAL
$$\textcolor{#23d18b}{\tt{tests/test\_platform.py}}$$ $$\textcolor{#23d18b}{\tt{TestPlatformHelperRetry.test\_success\_on\_first\_attempt}}$$ $$\textcolor{#23d18b}{\tt{2}}$$ $$\textcolor{#23d18b}{\tt{2}}$$
$$\textcolor{#23d18b}{\tt{tests/test\_platform.py}}$$ $$\textcolor{#23d18b}{\tt{TestPlatformHelperRetry.test\_retry\_on\_connection\_error}}$$ $$\textcolor{#23d18b}{\tt{2}}$$ $$\textcolor{#23d18b}{\tt{2}}$$
$$\textcolor{#23d18b}{\tt{tests/test\_platform.py}}$$ $$\textcolor{#23d18b}{\tt{TestPlatformHelperRetry.test\_non\_retryable\_http\_error}}$$ $$\textcolor{#23d18b}{\tt{1}}$$ $$\textcolor{#23d18b}{\tt{1}}$$
$$\textcolor{#23d18b}{\tt{tests/test\_platform.py}}$$ $$\textcolor{#23d18b}{\tt{TestPlatformHelperRetry.test\_retryable\_http\_errors}}$$ $$\textcolor{#23d18b}{\tt{3}}$$ $$\textcolor{#23d18b}{\tt{3}}$$
$$\textcolor{#23d18b}{\tt{tests/test\_platform.py}}$$ $$\textcolor{#23d18b}{\tt{TestPlatformHelperRetry.test\_post\_method\_retry}}$$ $$\textcolor{#23d18b}{\tt{1}}$$ $$\textcolor{#23d18b}{\tt{1}}$$
$$\textcolor{#23d18b}{\tt{tests/test\_platform.py}}$$ $$\textcolor{#23d18b}{\tt{TestPlatformHelperRetry.test\_retry\_logging}}$$ $$\textcolor{#23d18b}{\tt{1}}$$ $$\textcolor{#23d18b}{\tt{1}}$$
$$\textcolor{#23d18b}{\tt{tests/test\_prompt.py}}$$ $$\textcolor{#23d18b}{\tt{TestPromptToolRetry.test\_success\_on\_first\_attempt}}$$ $$\textcolor{#23d18b}{\tt{1}}$$ $$\textcolor{#23d18b}{\tt{1}}$$
$$\textcolor{#23d18b}{\tt{tests/test\_prompt.py}}$$ $$\textcolor{#23d18b}{\tt{TestPromptToolRetry.test\_retry\_on\_errors}}$$ $$\textcolor{#23d18b}{\tt{2}}$$ $$\textcolor{#23d18b}{\tt{2}}$$
$$\textcolor{#23d18b}{\tt{tests/test\_prompt.py}}$$ $$\textcolor{#23d18b}{\tt{TestPromptToolRetry.test\_wrapper\_methods\_retry}}$$ $$\textcolor{#23d18b}{\tt{4}}$$ $$\textcolor{#23d18b}{\tt{4}}$$
$$\textcolor{#23d18b}{\tt{tests/utils/test\_retry\_utils.py}}$$ $$\textcolor{#23d18b}{\tt{TestIsRetryableError.test\_connection\_error\_is\_retryable}}$$ $$\textcolor{#23d18b}{\tt{1}}$$ $$\textcolor{#23d18b}{\tt{1}}$$
$$\textcolor{#23d18b}{\tt{tests/utils/test\_retry\_utils.py}}$$ $$\textcolor{#23d18b}{\tt{TestIsRetryableError.test\_timeout\_is\_retryable}}$$ $$\textcolor{#23d18b}{\tt{1}}$$ $$\textcolor{#23d18b}{\tt{1}}$$
$$\textcolor{#23d18b}{\tt{tests/utils/test\_retry\_utils.py}}$$ $$\textcolor{#23d18b}{\tt{TestIsRetryableError.test\_http\_error\_retryable\_status\_codes}}$$ $$\textcolor{#23d18b}{\tt{3}}$$ $$\textcolor{#23d18b}{\tt{3}}$$
$$\textcolor{#23d18b}{\tt{tests/utils/test\_retry\_utils.py}}$$ $$\textcolor{#23d18b}{\tt{TestIsRetryableError.test\_http\_error\_non\_retryable\_status\_codes}}$$ $$\textcolor{#23d18b}{\tt{5}}$$ $$\textcolor{#23d18b}{\tt{5}}$$
$$\textcolor{#23d18b}{\tt{tests/utils/test\_retry\_utils.py}}$$ $$\textcolor{#23d18b}{\tt{TestIsRetryableError.test\_http\_error\_without\_response}}$$ $$\textcolor{#23d18b}{\tt{1}}$$ $$\textcolor{#23d18b}{\tt{1}}$$
$$\textcolor{#23d18b}{\tt{tests/utils/test\_retry\_utils.py}}$$ $$\textcolor{#23d18b}{\tt{TestIsRetryableError.test\_os\_error\_retryable\_errno}}$$ $$\textcolor{#23d18b}{\tt{5}}$$ $$\textcolor{#23d18b}{\tt{5}}$$
$$\textcolor{#23d18b}{\tt{tests/utils/test\_retry\_utils.py}}$$ $$\textcolor{#23d18b}{\tt{TestIsRetryableError.test\_os\_error\_non\_retryable\_errno}}$$ $$\textcolor{#23d18b}{\tt{1}}$$ $$\textcolor{#23d18b}{\tt{1}}$$
$$\textcolor{#23d18b}{\tt{tests/utils/test\_retry\_utils.py}}$$ $$\textcolor{#23d18b}{\tt{TestIsRetryableError.test\_other\_exception\_not\_retryable}}$$ $$\textcolor{#23d18b}{\tt{1}}$$ $$\textcolor{#23d18b}{\tt{1}}$$
$$\textcolor{#23d18b}{\tt{tests/utils/test\_retry\_utils.py}}$$ $$\textcolor{#23d18b}{\tt{TestCalculateDelay.test\_exponential\_backoff\_without\_jitter}}$$ $$\textcolor{#23d18b}{\tt{1}}$$ $$\textcolor{#23d18b}{\tt{1}}$$
$$\textcolor{#23d18b}{\tt{tests/utils/test\_retry\_utils.py}}$$ $$\textcolor{#23d18b}{\tt{TestCalculateDelay.test\_exponential\_backoff\_with\_jitter}}$$ $$\textcolor{#23d18b}{\tt{1}}$$ $$\textcolor{#23d18b}{\tt{1}}$$
$$\textcolor{#23d18b}{\tt{tests/utils/test\_retry\_utils.py}}$$ $$\textcolor{#23d18b}{\tt{TestCalculateDelay.test\_max\_delay\_cap}}$$ $$\textcolor{#23d18b}{\tt{1}}$$ $$\textcolor{#23d18b}{\tt{1}}$$
$$\textcolor{#23d18b}{\tt{tests/utils/test\_retry\_utils.py}}$$ $$\textcolor{#23d18b}{\tt{TestCalculateDelay.test\_max\_delay\_cap\_with\_jitter}}$$ $$\textcolor{#23d18b}{\tt{1}}$$ $$\textcolor{#23d18b}{\tt{1}}$$
$$\textcolor{#23d18b}{\tt{tests/utils/test\_retry\_utils.py}}$$ $$\textcolor{#23d18b}{\tt{TestRetryWithExponentialBackoff.test\_successful\_call\_first\_attempt}}$$ $$\textcolor{#23d18b}{\tt{1}}$$ $$\textcolor{#23d18b}{\tt{1}}$$
$$\textcolor{#23d18b}{\tt{tests/utils/test\_retry\_utils.py}}$$ $$\textcolor{#23d18b}{\tt{TestRetryWithExponentialBackoff.test\_retry\_after\_transient\_failure}}$$ $$\textcolor{#23d18b}{\tt{1}}$$ $$\textcolor{#23d18b}{\tt{1}}$$
$$\textcolor{#23d18b}{\tt{tests/utils/test\_retry\_utils.py}}$$ $$\textcolor{#23d18b}{\tt{TestRetryWithExponentialBackoff.test\_max\_retries\_exceeded}}$$ $$\textcolor{#23d18b}{\tt{1}}$$ $$\textcolor{#23d18b}{\tt{1}}$$
$$\textcolor{#23d18b}{\tt{tests/utils/test\_retry\_utils.py}}$$ $$\textcolor{#23d18b}{\tt{TestRetryWithExponentialBackoff.test\_retry\_with\_custom\_predicate}}$$ $$\textcolor{#23d18b}{\tt{1}}$$ $$\textcolor{#23d18b}{\tt{1}}$$
$$\textcolor{#23d18b}{\tt{tests/utils/test\_retry\_utils.py}}$$ $$\textcolor{#23d18b}{\tt{TestRetryWithExponentialBackoff.test\_no\_retry\_with\_predicate\_false}}$$ $$\textcolor{#23d18b}{\tt{1}}$$ $$\textcolor{#23d18b}{\tt{1}}$$
$$\textcolor{#23d18b}{\tt{tests/utils/test\_retry\_utils.py}}$$ $$\textcolor{#23d18b}{\tt{TestRetryWithExponentialBackoff.test\_exception\_not\_in\_tuple\_not\_retried}}$$ $$\textcolor{#23d18b}{\tt{1}}$$ $$\textcolor{#23d18b}{\tt{1}}$$
$$\textcolor{#23d18b}{\tt{tests/utils/test\_retry\_utils.py}}$$ $$\textcolor{#23d18b}{\tt{TestCreateRetryDecorator.test\_default\_configuration}}$$ $$\textcolor{#23d18b}{\tt{1}}$$ $$\textcolor{#23d18b}{\tt{1}}$$
$$\textcolor{#23d18b}{\tt{tests/utils/test\_retry\_utils.py}}$$ $$\textcolor{#23d18b}{\tt{TestCreateRetryDecorator.test\_environment\_variable\_configuration}}$$ $$\textcolor{#23d18b}{\tt{1}}$$ $$\textcolor{#23d18b}{\tt{1}}$$
$$\textcolor{#23d18b}{\tt{tests/utils/test\_retry\_utils.py}}$$ $$\textcolor{#23d18b}{\tt{TestCreateRetryDecorator.test\_invalid\_max\_retries}}$$ $$\textcolor{#23d18b}{\tt{1}}$$ $$\textcolor{#23d18b}{\tt{1}}$$
$$\textcolor{#23d18b}{\tt{tests/utils/test\_retry\_utils.py}}$$ $$\textcolor{#23d18b}{\tt{TestCreateRetryDecorator.test\_invalid\_base\_delay}}$$ $$\textcolor{#23d18b}{\tt{1}}$$ $$\textcolor{#23d18b}{\tt{1}}$$
$$\textcolor{#23d18b}{\tt{tests/utils/test\_retry\_utils.py}}$$ $$\textcolor{#23d18b}{\tt{TestCreateRetryDecorator.test\_invalid\_multiplier}}$$ $$\textcolor{#23d18b}{\tt{1}}$$ $$\textcolor{#23d18b}{\tt{1}}$$
$$\textcolor{#23d18b}{\tt{tests/utils/test\_retry\_utils.py}}$$ $$\textcolor{#23d18b}{\tt{TestCreateRetryDecorator.test\_jitter\_values}}$$ $$\textcolor{#23d18b}{\tt{2}}$$ $$\textcolor{#23d18b}{\tt{2}}$$
$$\textcolor{#23d18b}{\tt{tests/utils/test\_retry\_utils.py}}$$ $$\textcolor{#23d18b}{\tt{TestCreateRetryDecorator.test\_custom\_exceptions\_only}}$$ $$\textcolor{#23d18b}{\tt{1}}$$ $$\textcolor{#23d18b}{\tt{1}}$$
$$\textcolor{#23d18b}{\tt{tests/utils/test\_retry\_utils.py}}$$ $$\textcolor{#23d18b}{\tt{TestCreateRetryDecorator.test\_custom\_predicate\_only}}$$ $$\textcolor{#23d18b}{\tt{1}}$$ $$\textcolor{#23d18b}{\tt{1}}$$
$$\textcolor{#23d18b}{\tt{tests/utils/test\_retry\_utils.py}}$$ $$\textcolor{#23d18b}{\tt{TestCreateRetryDecorator.test\_both\_exceptions\_and\_predicate}}$$ $$\textcolor{#23d18b}{\tt{1}}$$ $$\textcolor{#23d18b}{\tt{1}}$$
$$\textcolor{#23d18b}{\tt{tests/utils/test\_retry\_utils.py}}$$ $$\textcolor{#23d18b}{\tt{TestCreateRetryDecorator.test\_exceptions\_match\_but\_predicate\_false}}$$ $$\textcolor{#23d18b}{\tt{1}}$$ $$\textcolor{#23d18b}{\tt{1}}$$
$$\textcolor{#23d18b}{\tt{tests/utils/test\_retry\_utils.py}}$$ $$\textcolor{#23d18b}{\tt{TestPreconfiguredDecorators.test\_retry\_platform\_service\_call\_exists}}$$ $$\textcolor{#23d18b}{\tt{1}}$$ $$\textcolor{#23d18b}{\tt{1}}$$
$$\textcolor{#23d18b}{\tt{tests/utils/test\_retry\_utils.py}}$$ $$\textcolor{#23d18b}{\tt{TestPreconfiguredDecorators.test\_retry\_prompt\_service\_call\_exists}}$$ $$\textcolor{#23d18b}{\tt{1}}$$ $$\textcolor{#23d18b}{\tt{1}}$$
$$\textcolor{#23d18b}{\tt{tests/utils/test\_retry\_utils.py}}$$ $$\textcolor{#23d18b}{\tt{TestPreconfiguredDecorators.test\_platform\_service\_decorator\_retries\_on\_connection\_error}}$$ $$\textcolor{#23d18b}{\tt{1}}$$ $$\textcolor{#23d18b}{\tt{1}}$$
$$\textcolor{#23d18b}{\tt{tests/utils/test\_retry\_utils.py}}$$ $$\textcolor{#23d18b}{\tt{TestPreconfiguredDecorators.test\_prompt\_service\_decorator\_retries\_on\_timeout}}$$ $$\textcolor{#23d18b}{\tt{1}}$$ $$\textcolor{#23d18b}{\tt{1}}$$
$$\textcolor{#23d18b}{\tt{tests/utils/test\_retry\_utils.py}}$$ $$\textcolor{#23d18b}{\tt{TestRetryLogging.test\_warning\_logged\_on\_retry}}$$ $$\textcolor{#23d18b}{\tt{1}}$$ $$\textcolor{#23d18b}{\tt{1}}$$
$$\textcolor{#23d18b}{\tt{tests/utils/test\_retry\_utils.py}}$$ $$\textcolor{#23d18b}{\tt{TestRetryLogging.test\_info\_logged\_on\_success\_after\_retry}}$$ $$\textcolor{#23d18b}{\tt{1}}$$ $$\textcolor{#23d18b}{\tt{1}}$$
$$\textcolor{#23d18b}{\tt{tests/utils/test\_retry\_utils.py}}$$ $$\textcolor{#23d18b}{\tt{TestRetryLogging.test\_exception\_logged\_on\_giving\_up}}$$ $$\textcolor{#23d18b}{\tt{1}}$$ $$\textcolor{#23d18b}{\tt{1}}$$
$$\textcolor{#23d18b}{\tt{TOTAL}}$$ $$\textcolor{#23d18b}{\tt{63}}$$ $$\textcolor{#23d18b}{\tt{63}}$$

Comment on lines +82 to +83
ARG VERSION=dev
ENV UNSTRACT_APPS_VERSION=${VERSION}
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

P2 UNSTRACT_APPS_VERSION env var set but never consumed

UNSTRACT_APPS_VERSION is now baked into every image as a runtime environment variable, but a search of the entire codebase shows zero consumers of this variable — no Python code, Helm chart, or CI script reads it.

Setting an ENV here does still create a Docker layer (unlike LABEL), which means it will be rebuilt every time VERSION changes. The cache-fix goal is fully achieved by simply omitting the ARG/ENV entirely at the end. If the intent is to make the version introspectable at runtime (e.g. for a /health or /version endpoint), that's a good goal — but the consuming code hasn't been added yet.

The same applies to all seven Dockerfiles in this PR (platform.Dockerfile, prompt.Dockerfile, runner.Dockerfile, tool-sidecar.Dockerfile, worker-unified.Dockerfile, x2text.Dockerfile).

If you want to keep it for future use, consider adding a comment explaining its purpose so reviewers don't treat it as dead configuration.

Prompt To Fix With AI
This is a comment left during a code review.
Path: docker/dockerfiles/backend.Dockerfile
Line: 82-83

Comment:
**`UNSTRACT_APPS_VERSION` env var set but never consumed**

`UNSTRACT_APPS_VERSION` is now baked into every image as a runtime environment variable, but a search of the entire codebase shows zero consumers of this variable — no Python code, Helm chart, or CI script reads it.

Setting an `ENV` here does still create a Docker layer (unlike `LABEL`), which means it will be rebuilt every time `VERSION` changes. The cache-fix goal is fully achieved by simply omitting the `ARG`/`ENV` entirely at the end. If the intent is to make the version introspectable at runtime (e.g. for a `/health` or `/version` endpoint), that's a good goal — but the consuming code hasn't been added yet.

The same applies to all seven Dockerfiles in this PR (`platform.Dockerfile`, `prompt.Dockerfile`, `runner.Dockerfile`, `tool-sidecar.Dockerfile`, `worker-unified.Dockerfile`, `x2text.Dockerfile`).

If you want to keep it for future use, consider adding a comment explaining its purpose so reviewers don't treat it as dead configuration.

How can I resolve this? If you propose a fix, please make it concise.

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@chandrasekharan-zipstack isn't this a valid comment? Why this needs to be env? Can't we add lables at the end?

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Yes, the comment is valid. Here's the key distinction:

ENV vs LABEL at the end:

  • ENV UNSTRACT_APPS_VERSION=${VERSION} → Creates a runtime environment variable inside the container. Useful if application code needs to read it (e.g., /health endpoint showing version)
  • LABEL version="${VERSION}" → Pure image metadata, doesn't affect runtime. Can be inspected with docker inspect

Both would rebuild that final layer when VERSION changes, which is fine since all the expensive layers (dependencies, code) are already cached.

The issue: No code currently reads UNSTRACT_APPS_VERSION from the environment, so it's unused runtime pollution. If the goal is just version metadata, LABEL at the end would be cleaner:

# At the very end
ARG VERSION=dev
LABEL version="${VERSION}"

If there's a plan to consume this in Python code (e.g., expose in an API), then ENV makes sense but should be documented.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@ritwik-g the idea of adding it as an env was so that, we can later support showing the version in the UI as well by reading this. If its a label, we can't read this env
cc: @hari-kuriakose

@sonarqubecloud
Copy link

Copy link
Contributor

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🧹 Nitpick comments (1)
docker/dockerfiles/worker-unified.Dockerfile (1)

20-29: Harden apt install against transient CI network timeouts.

Given the observed timeout failures, add apt retries/timeouts here to reduce flaky builds.

🔧 Suggested resilience tweak for apt network flakiness
 RUN apt-get update \
-    && apt-get --no-install-recommends install -y \
+    && apt-get -o Acquire::Retries=5 -o Acquire::http::Timeout="30" --no-install-recommends install -y \
        build-essential \
        curl \
        gcc \
        libmagic-dev \
        libssl-dev \
        pkg-config \
     && apt-get clean \
     && rm -rf /var/lib/apt/lists/* /var/cache/apt/archives/*
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@docker/dockerfiles/worker-unified.Dockerfile` around lines 20 - 29, The apt
commands in the RUN layer (the "RUN apt-get update" and "apt-get
--no-install-recommends install -y" lines) are failing intermittently due to
transient network timeouts; make them more resilient by adding apt retry and
timeout options (for example use -o Acquire::Retries=3 and -o
Acquire::http::Timeout=30/-o Acquire::https::Timeout=30) to both the update and
install invocations, and consider setting DEBIAN_FRONTEND=noninteractive for the
layer; update the RUN line that contains "apt-get update" and "apt-get
--no-install-recommends install -y" to include these apt options and retry
semantics so CI builds are less flaky.
🤖 Prompt for all review comments with AI agents
Verify each finding against the current code and only fix it if needed.

Nitpick comments:
In `@docker/dockerfiles/worker-unified.Dockerfile`:
- Around line 20-29: The apt commands in the RUN layer (the "RUN apt-get update"
and "apt-get --no-install-recommends install -y" lines) are failing
intermittently due to transient network timeouts; make them more resilient by
adding apt retry and timeout options (for example use -o Acquire::Retries=3 and
-o Acquire::http::Timeout=30/-o Acquire::https::Timeout=30) to both the update
and install invocations, and consider setting DEBIAN_FRONTEND=noninteractive for
the layer; update the RUN line that contains "apt-get update" and "apt-get
--no-install-recommends install -y" to include these apt options and retry
semantics so CI builds are less flaky.

ℹ️ Review info
⚙️ Run configuration

Configuration used: Organization UI

Review profile: CHILL

Plan: Pro

Run ID: 2b15eda5-b782-4825-a007-49b82d869dd0

📥 Commits

Reviewing files that changed from the base of the PR and between c71bd40 and e281e57.

📒 Files selected for processing (7)
  • docker/dockerfiles/backend.Dockerfile
  • docker/dockerfiles/platform.Dockerfile
  • docker/dockerfiles/prompt.Dockerfile
  • docker/dockerfiles/runner.Dockerfile
  • docker/dockerfiles/tool-sidecar.Dockerfile
  • docker/dockerfiles/worker-unified.Dockerfile
  • docker/dockerfiles/x2text.Dockerfile
🚧 Files skipped from review as they are similar to previous changes (4)
  • docker/dockerfiles/prompt.Dockerfile
  • docker/dockerfiles/tool-sidecar.Dockerfile
  • docker/dockerfiles/x2text.Dockerfile
  • docker/dockerfiles/platform.Dockerfile

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants