-
Notifications
You must be signed in to change notification settings - Fork 0
Expand file tree
/
Copy pathDockerfile
More file actions
139 lines (121 loc) · 7.79 KB
/
Copy pathDockerfile
File metadata and controls
139 lines (121 loc) · 7.79 KB
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
# syntax=docker/dockerfile:1
#
# Production image for the three Quarkus services. ONE Dockerfile parameterised by SERVICE, which
# matches the in-repo precedent: Dockerfile.dev is already one image parameterised by compose.
#
# docker build --build-arg SERVICE=gateway -t spire-gateway .
# docker build --build-arg SERVICE=orchestrator -t spire-orchestrator .
# docker build --build-arg SERVICE=review-worker -t spire-review-worker .
#
ARG SERVICE
FROM eclipse-temurin:25-jdk AS build
ARG SERVICE
WORKDIR /workspace
# Build files first, so the dependency layer caches independently of source edits.
COPY gradlew settings.gradle.kts gradle.properties build.gradle.kts ./
COPY gradle/ gradle/
COPY spire-agent-image/build.gradle.kts spire-agent-image/
COPY spire-arch/build.gradle.kts spire-arch/
COPY spire-context-code/build.gradle.kts spire-context-code/
COPY spire-context-confluence/build.gradle.kts spire-context-confluence/
COPY spire-context-github/build.gradle.kts spire-context-github/
COPY spire-context-gitlab/build.gradle.kts spire-context-gitlab/
COPY spire-context-jira/build.gradle.kts spire-context-jira/
COPY spire-contract/build.gradle.kts spire-contract/
COPY spire-diff/build.gradle.kts spire-diff/
# Not a dependency of any service — but settings.gradle.kts includes it, and Gradle refuses to
# configure an included project whose directory is absent. Every module in settings must appear in
# this list or `:spire-<service>:dependencies` fails before a single service class is compiled.
COPY spire-e2e/build.gradle.kts spire-e2e/
COPY spire-encryption/build.gradle.kts spire-encryption/
COPY spire-gateway/build.gradle.kts spire-gateway/
COPY spire-harness/build.gradle.kts spire-harness/
COPY spire-harness-codex/build.gradle.kts spire-harness-codex/
COPY spire-http/build.gradle.kts spire-http/
COPY spire-llm/build.gradle.kts spire-llm/
COPY spire-orchestrator/build.gradle.kts spire-orchestrator/
COPY spire-publisher/build.gradle.kts spire-publisher/
COPY spire-review-worker/build.gradle.kts spire-review-worker/
COPY spire-run-worker/build.gradle.kts spire-run-worker/
COPY spire-runtime/build.gradle.kts spire-runtime/
COPY spire-runtime-docker/build.gradle.kts spire-runtime-docker/
COPY spire-scm-bitbucket/build.gradle.kts spire-scm-bitbucket/
COPY spire-scm-github/build.gradle.kts spire-scm-github/
COPY spire-scm-gitlab/build.gradle.kts spire-scm-gitlab/
COPY spire-secrets/build.gradle.kts spire-secrets/
COPY spire-worksource/build.gradle.kts spire-worksource/
COPY spire-worksource-github/build.gradle.kts spire-worksource-github/
COPY spire-worksource-jira/build.gradle.kts spire-worksource-jira/
COPY spire-worksource-gitlab/build.gradle.kts spire-worksource-gitlab/
COPY spire-workspace/build.gradle.kts spire-workspace/
# A Windows checkout gives gradlew CRLF and /bin/sh then rejects the shebang with
# "bad interpreter: /bin/sh^M". CI on Linux never sees this; a local deploy/compose.yml build does.
# Same normalisation Dockerfile.dev applies, for the same reason.
RUN sed -i 's/\r$//' gradlew && chmod +x gradlew
# Resolve dependencies with only the build files present, so this layer survives every source edit.
RUN ./gradlew --no-daemon --console=plain :spire-${SERVICE}:dependencies \
--configuration runtimeClasspath > /dev/null
COPY . .
# Flyway checksums are a CRC over file bytes, so a CRLF migration hashes differently from the LF the
# database was migrated with and boot fails on a checksum mismatch.
RUN sed -i 's/\r$//' gradlew \
&& find . -path '*/db/migration/*.sql' -exec sed -i 's/\r$//' {} +
# Tests already ran in ci.yml. Re-running here would run them once per architecture under QEMU.
RUN ./gradlew --no-daemon --console=plain :spire-${SERVICE}:build -x test
FROM eclipse-temurin:25-jre-alpine
ARG SERVICE
LABEL org.opencontainers.image.title="spire-${SERVICE}" \
org.opencontainers.image.description="Code Spire ${SERVICE} — source-available, self-hosted AI code reviewer" \
org.opencontainers.image.source="https://github.com/artyomsv/code-spire" \
org.opencontainers.image.licenses="FSL-1.1-ALv2"
# Each service's application.yml sets quarkus.http.port from its own ${*_HTTP_PORT:3408x} default.
# This targets quarkus.http.port directly and beats that default, so all three images listen where
# EXPOSE and HEALTHCHECK say they do. Without it the container reports unhealthy forever and
# compose's `depends_on: service_healthy` never releases. One variable covers all three services.
ENV QUARKUS_HTTP_PORT=8080 \
QUARKUS_HTTP_HOST=0.0.0.0 \
QUARKUS_PROFILE=prod
# eclipse-temurin retags on its own cadence, which is slower than Alpine's package index moves. The
# gap is where every OS-level CVE Trivy reports on these three images comes from — libexpat, openssl
# and p11-kit, none of which this image installs or uses directly, all inherited from the base. An
# upgrade here closes them at build time instead of waiting for an upstream retag that may never come
# for a given tag.
#
# It does cost reproducibility: two builds of the same commit a week apart can now carry different
# package versions. That is the accepted trade — the alternative is pinning each package to a version
# that itself goes stale, which is the same treadmill with an extra step. The image digest is what
# deployments pin (deploy/ resolves to sha-<short>), so a given deployed artifact is still exact.
#
# **APK_UPGRADE_BUST is what makes any of the above true, and without it none of it was.** This layer
# has no input that changes, so BuildKit restored it from the gha cache on every build and the upgrade
# ran exactly once — the first time. Its only cache key was the base image, so the mitigation refreshed
# precisely when the base retagged, which is the event it exists to not wait for. Measured, not
# inferred: run 33810550375 logged `#48 [stage-1 2/10] RUN apk --no-cache upgrade` followed by
# `#48 CACHED` for all three services, and the 102 open Trivy alerts were openssl 3.5.7-r0 and
# libexpat 2.8.3-r0 while the live Alpine index had 3.5.8-r0 and 2.8.4-r0 for both. docker.yml passes
# github.run_id here so the layer re-executes once per build; ApkUpgradeIsNotCachedTest holds it to
# that. Echoed rather than merely declared because BuildKit keys a RUN on the args it actually
# references — an ARG the command never mentions changes no cache key and would leave this inert in a
# way that looks fixed.
ARG APK_UPGRADE_BUST=local
RUN echo "apk upgrade for build ${APK_UPGRADE_BUST}" && apk --no-cache upgrade
RUN addgroup -g 1001 spire && adduser -u 1001 -G spire -s /bin/sh -D spire
WORKDIR /app
# The fast-jar in four layers, lib/ first. lib/ is hundreds of MB of unchanging dependencies while
# app/ is about a megabyte of our own classes; copied as one directory, every code change re-pushes
# the whole thing, which on a JVM image is the difference between a seconds-long and a minutes-long
# push.
COPY --from=build --chown=1001:1001 /workspace/spire-${SERVICE}/build/quarkus-app/lib/ ./lib/
COPY --from=build --chown=1001:1001 /workspace/spire-${SERVICE}/build/quarkus-app/*.jar ./
COPY --from=build --chown=1001:1001 /workspace/spire-${SERVICE}/build/quarkus-app/app/ ./app/
COPY --from=build --chown=1001:1001 /workspace/spire-${SERVICE}/build/quarkus-app/quarkus/ ./quarkus/
# The SERVICE's own licence, not the repo root's — the root LICENSE is a pointer to LICENSING.md,
# while each deployable carries the FSL-1.1-ALv2 text the OCI label above declares (ADR-021).
COPY --from=build --chown=1001:1001 /workspace/spire-${SERVICE}/LICENSE ./LICENSE
COPY --from=build --chown=1001:1001 /workspace/NOTICE /workspace/LICENSING.md ./
USER 1001
EXPOSE 8080
# start-period covers JVM boot plus Flyway migration on a cold database.
HEALTHCHECK --interval=30s --timeout=3s --start-period=90s --retries=3 \
CMD wget -qO- http://localhost:8080/q/health/ready || exit 1
ENTRYPOINT ["java", "-jar", "/app/quarkus-run.jar"]