From 211bfc20ea3bbcaeee185a8e2acc549ab83bd97b Mon Sep 17 00:00:00 2001 From: Widthdom Date: Sat, 13 Jun 2026 23:29:28 +0900 Subject: [PATCH 1/4] Fix Docker image base pinning (#3492) --- Dockerfile | 14 +++++++++++--- changelog.d/unreleased/3492.fixed.md | 16 ++++++++++++++++ tests/CodeIndex.Tests/ReleaseWorkflowTests.cs | 9 ++++++++- 3 files changed, 35 insertions(+), 4 deletions(-) create mode 100644 changelog.d/unreleased/3492.fixed.md diff --git a/Dockerfile b/Dockerfile index 94660048d4..b674b3cd3e 100644 --- a/Dockerfile +++ b/Dockerfile @@ -1,4 +1,6 @@ -FROM mcr.microsoft.com/dotnet/sdk:8.0-alpine AS build +# Base image digests are multi-arch manifest list digests. Refresh with: +# docker buildx imagetools inspect mcr.microsoft.com/dotnet/:8.0-alpine +FROM mcr.microsoft.com/dotnet/sdk:8.0-alpine@sha256:d9f4f4a5d99a43799b500ee1365c370e3233822fbe7d43666715d9b5b5cda2ab AS build WORKDIR /src COPY . . @@ -18,9 +20,13 @@ RUN case "$TARGETARCH" in \ -p:PublishTrimmed=true \ --output /out -FROM mcr.microsoft.com/dotnet/runtime-deps:8.0-alpine AS runtime +FROM mcr.microsoft.com/dotnet/runtime-deps:8.0-alpine@sha256:7ec14bf41e70f3ca60f7b369b077636f642a0e6867caf28677d970e0abd9c6e6 AS runtime -RUN apk add --no-cache ca-certificates +RUN apk add --no-cache ca-certificates \ + && addgroup -S cdidx \ + && adduser -S -D -H -G cdidx -h /repo cdidx \ + && mkdir -p /repo \ + && chown cdidx:cdidx /repo WORKDIR /repo COPY --from=build /out/ /usr/local/lib/cdidx/ @@ -28,5 +34,7 @@ COPY LICENSE COMMERCIAL_LICENSE.md INTEGRATION_POLICY.md TRADEMARKS.md /usr/loca COPY LICENSES/ /usr/local/lib/cdidx/LICENSES/ RUN ln -s /usr/local/lib/cdidx/cdidx /usr/local/bin/cdidx +USER cdidx:cdidx + ENTRYPOINT ["cdidx"] CMD ["--help"] diff --git a/changelog.d/unreleased/3492.fixed.md b/changelog.d/unreleased/3492.fixed.md new file mode 100644 index 0000000000..b5f4b09a27 --- /dev/null +++ b/changelog.d/unreleased/3492.fixed.md @@ -0,0 +1,16 @@ +--- +category: fixed +issues: + - 3492 +affected: + - Dockerfile + - tests/CodeIndex.Tests/ReleaseWorkflowTests.cs +--- + +## English + +- **Docker images now pin .NET base images and run as a non-root user (#3492)** — the container build uses explicit multi-arch MCR image digests and the runtime stage switches to a dedicated `cdidx` user after installation. + +## 日本語 + +- **Docker image が .NET base image を digest 固定し、non-root user で実行されるようになりました (#3492)** — container build は MCR の multi-arch image digest を明示し、runtime stage はインストール後に専用の `cdidx` user へ切り替えます。 diff --git a/tests/CodeIndex.Tests/ReleaseWorkflowTests.cs b/tests/CodeIndex.Tests/ReleaseWorkflowTests.cs index bcfc7efc2f..90fdfc5135 100644 --- a/tests/CodeIndex.Tests/ReleaseWorkflowTests.cs +++ b/tests/CodeIndex.Tests/ReleaseWorkflowTests.cs @@ -480,7 +480,14 @@ public void ReleaseWorkflow_PublishesOfficialContainerImage() Assert.Contains("ghcr.io/widthdom/codeindex:latest", workflow); Assert.Contains("tags: ${{ steps.image-tags.outputs.tags }}", workflow); Assert.Contains("*-*) ;;", workflow); - Assert.Contains("FROM mcr.microsoft.com/dotnet/sdk:8.0-alpine AS build", dockerfile); + Assert.Contains("docker buildx imagetools inspect mcr.microsoft.com/dotnet/:8.0-alpine", dockerfile); + Assert.Contains("FROM mcr.microsoft.com/dotnet/sdk:8.0-alpine@sha256:d9f4f4a5d99a43799b500ee1365c370e3233822fbe7d43666715d9b5b5cda2ab AS build", dockerfile); + Assert.Contains("FROM mcr.microsoft.com/dotnet/runtime-deps:8.0-alpine@sha256:7ec14bf41e70f3ca60f7b369b077636f642a0e6867caf28677d970e0abd9c6e6 AS runtime", dockerfile); + Assert.DoesNotContain("FROM mcr.microsoft.com/dotnet/sdk:8.0-alpine AS build", dockerfile); + Assert.DoesNotContain("FROM mcr.microsoft.com/dotnet/runtime-deps:8.0-alpine AS runtime", dockerfile); + Assert.Contains("adduser -S -D -H -G cdidx -h /repo cdidx", dockerfile); + Assert.Contains("chown cdidx:cdidx /repo", dockerfile); + Assert.Contains("USER cdidx:cdidx", dockerfile); Assert.Contains("ARG TARGETARCH=amd64", dockerfile); Assert.Contains("linux-musl-x64", dockerfile); Assert.Contains("linux-musl-arm64", dockerfile); From 42d31d67daba1ea35e48d4857d25c58d3c1c1a0d Mon Sep 17 00:00:00 2001 From: Widthdom Date: Sat, 13 Jun 2026 23:33:13 +0900 Subject: [PATCH 2/4] Narrow Docker build context (#3493) --- .dockerignore | 27 +++++++++++++++++++ Dockerfile | 8 ++++-- changelog.d/unreleased/3493.fixed.md | 17 ++++++++++++ tests/CodeIndex.Tests/ReleaseWorkflowTests.cs | 12 +++++++++ 4 files changed, 62 insertions(+), 2 deletions(-) create mode 100644 changelog.d/unreleased/3493.fixed.md diff --git a/.dockerignore b/.dockerignore index ecd8da4da0..14a939b671 100644 --- a/.dockerignore +++ b/.dockerignore @@ -1,9 +1,36 @@ +.git/ +.gitmodules +.gitattributes +.editorconfig .cdidx/ .github/ +.agents/ +.agent_harness/ +.claude/ +.codex/ .vs/ .vscode/ +.idea/ TestResults/ **/bin/ **/obj/ **/.DS_Store *.user + +tests/ +tools/ +docs/ +changelog.d/ +CodeIndex.sln +CHANGELOG.md +README.md +AGENTS.md +AGENT_GUIDE.md +CLAUDE.md +DEVELOPER_GUIDE.md +SELF_IMPROVEMENT.md +TESTING_GUIDE.md +*.md +!COMMERCIAL_LICENSE.md +!INTEGRATION_POLICY.md +!TRADEMARKS.md diff --git a/Dockerfile b/Dockerfile index b674b3cd3e..43b32af826 100644 --- a/Dockerfile +++ b/Dockerfile @@ -3,10 +3,14 @@ FROM mcr.microsoft.com/dotnet/sdk:8.0-alpine@sha256:d9f4f4a5d99a43799b500ee1365c370e3233822fbe7d43666715d9b5b5cda2ab AS build WORKDIR /src -COPY . . +COPY Directory.Build.props nuget.config version.json ./ +COPY src/CodeIndex/CodeIndex.csproj src/CodeIndex/packages.lock.json src/CodeIndex/ +RUN dotnet restore src/CodeIndex/CodeIndex.csproj +COPY src/CodeIndex/ src/CodeIndex/ +COPY LICENSE COMMERCIAL_LICENSE.md INTEGRATION_POLICY.md TRADEMARKS.md ./ +COPY LICENSES/ LICENSES/ ARG TARGETARCH=amd64 -RUN dotnet restore src/CodeIndex/CodeIndex.csproj RUN case "$TARGETARCH" in \ amd64) rid="linux-musl-x64" ;; \ arm64) rid="linux-musl-arm64" ;; \ diff --git a/changelog.d/unreleased/3493.fixed.md b/changelog.d/unreleased/3493.fixed.md new file mode 100644 index 0000000000..e70c0d3543 --- /dev/null +++ b/changelog.d/unreleased/3493.fixed.md @@ -0,0 +1,17 @@ +--- +category: fixed +issues: + - 3493 +affected: + - .dockerignore + - Dockerfile + - tests/CodeIndex.Tests/ReleaseWorkflowTests.cs +--- + +## English + +- **Docker builds now use a narrower context (#3493)** — the build stage copies only restore, source, version, and license inputs needed for the runtime image, while `.dockerignore` excludes VCS metadata, tests, docs, workflow files, changelog fragments, tools, and agent artifacts from the build context. + +## 日本語 + +- **Docker build context を絞り込みました (#3493)** — build stage は runtime image に必要な restore / source / version / license 入力だけをコピーし、`.dockerignore` は VCS metadata、tests、docs、workflow files、changelog fragments、tools、agent artifacts を build context から除外します。 diff --git a/tests/CodeIndex.Tests/ReleaseWorkflowTests.cs b/tests/CodeIndex.Tests/ReleaseWorkflowTests.cs index 90fdfc5135..6253ae44a1 100644 --- a/tests/CodeIndex.Tests/ReleaseWorkflowTests.cs +++ b/tests/CodeIndex.Tests/ReleaseWorkflowTests.cs @@ -467,6 +467,7 @@ public void ReleaseWorkflow_PublishesOfficialContainerImage() var root = GetRepositoryRoot(); var workflow = File.ReadAllText(Path.Combine(root, ".github", "workflows", "release.yml")); var dockerfile = File.ReadAllText(Path.Combine(root, "Dockerfile")); + var dockerignore = File.ReadAllText(Path.Combine(root, ".dockerignore")); var project = File.ReadAllText(Path.Combine(root, "src", "CodeIndex", "CodeIndex.csproj")); Assert.Contains("publish-container:", workflow); @@ -488,6 +489,17 @@ public void ReleaseWorkflow_PublishesOfficialContainerImage() Assert.Contains("adduser -S -D -H -G cdidx -h /repo cdidx", dockerfile); Assert.Contains("chown cdidx:cdidx /repo", dockerfile); Assert.Contains("USER cdidx:cdidx", dockerfile); + Assert.DoesNotContain("COPY . .", dockerfile); + Assert.Contains("COPY Directory.Build.props nuget.config version.json ./", dockerfile); + Assert.Contains("COPY src/CodeIndex/CodeIndex.csproj src/CodeIndex/packages.lock.json src/CodeIndex/", dockerfile); + Assert.Contains("COPY src/CodeIndex/ src/CodeIndex/", dockerfile); + Assert.Contains(".git/", dockerignore); + Assert.Contains("tests/", dockerignore); + Assert.Contains("tools/", dockerignore); + Assert.Contains("docs/", dockerignore); + Assert.Contains("changelog.d/", dockerignore); + Assert.Contains("*.md", dockerignore); + Assert.Contains("!COMMERCIAL_LICENSE.md", dockerignore); Assert.Contains("ARG TARGETARCH=amd64", dockerfile); Assert.Contains("linux-musl-x64", dockerfile); Assert.Contains("linux-musl-arm64", dockerfile); From 5c91f83cdb07201cf68f6d2812e65b2517f44082 Mon Sep 17 00:00:00 2001 From: Widthdom Date: Sat, 13 Jun 2026 23:50:21 +0900 Subject: [PATCH 3/4] Preserve Docker metadata with narrow context (#3493) --- .github/workflows/release.yml | 16 ++++++++++++++++ Dockerfile | 7 +++++++ changelog.d/unreleased/3493.fixed.md | 6 ++++-- src/CodeIndex/CodeIndex.csproj | 14 ++++++++++---- tests/CodeIndex.Tests/ReleaseWorkflowTests.cs | 13 +++++++++++++ 5 files changed, 50 insertions(+), 6 deletions(-) diff --git a/.github/workflows/release.yml b/.github/workflows/release.yml index caa45da949..540adcae42 100644 --- a/.github/workflows/release.yml +++ b/.github/workflows/release.yml @@ -1085,6 +1085,18 @@ jobs: echo "EOF" } >> "$GITHUB_OUTPUT" + - name: Extract container build metadata + id: container-metadata + run: | + set -euo pipefail + echo "commit=$(git rev-parse --short=7 HEAD)" >> "$GITHUB_OUTPUT" + echo "date=$(git show -s --format=%cd --date=format:%Y-%m-%d HEAD)" >> "$GITHUB_OUTPUT" + if git diff-index --quiet HEAD -- ":/" ":(top,exclude,glob)**/packages.lock.json"; then + echo "dirty=clean" >> "$GITHUB_OUTPUT" + else + echo "dirty=dirty" >> "$GITHUB_OUTPUT" + fi + - name: Set up Docker Buildx uses: docker/setup-buildx-action@d7f5e7f509e45cec5c76c4d5afdd7de93d0b3df5 # v4 @@ -1104,6 +1116,10 @@ jobs: provenance: mode=max sbom: true tags: ${{ steps.image-tags.outputs.tags }} + build-args: | + CDIDX_BUILD_COMMIT=${{ steps.container-metadata.outputs.commit }} + CDIDX_BUILD_DATE=${{ steps.container-metadata.outputs.date }} + CDIDX_BUILD_DIRTY=${{ steps.container-metadata.outputs.dirty }} publish-homebrew: if: github.repository == 'Widthdom/CodeIndex' diff --git a/Dockerfile b/Dockerfile index 43b32af826..ba1416c5a6 100644 --- a/Dockerfile +++ b/Dockerfile @@ -11,17 +11,24 @@ COPY LICENSE COMMERCIAL_LICENSE.md INTEGRATION_POLICY.md TRADEMARKS.md ./ COPY LICENSES/ LICENSES/ ARG TARGETARCH=amd64 +ARG CDIDX_BUILD_COMMIT=unknown +ARG CDIDX_BUILD_DATE +ARG CDIDX_BUILD_DIRTY=unknown RUN case "$TARGETARCH" in \ amd64) rid="linux-musl-x64" ;; \ arm64) rid="linux-musl-arm64" ;; \ *) echo "Unsupported container architecture: $TARGETARCH" >&2; exit 1 ;; \ esac && \ + build_date="${CDIDX_BUILD_DATE:-$(date -u +%Y-%m-%d)}" && \ dotnet publish src/CodeIndex/CodeIndex.csproj \ --configuration Release \ --runtime "$rid" \ --self-contained true \ -p:PublishSingleFile=true \ -p:PublishTrimmed=true \ + -p:CdidxBuildCommitOverride="$CDIDX_BUILD_COMMIT" \ + -p:CdidxBuildDateOverride="$build_date" \ + -p:CdidxBuildDirtyOverride="$CDIDX_BUILD_DIRTY" \ --output /out FROM mcr.microsoft.com/dotnet/runtime-deps:8.0-alpine@sha256:7ec14bf41e70f3ca60f7b369b077636f642a0e6867caf28677d970e0abd9c6e6 AS runtime diff --git a/changelog.d/unreleased/3493.fixed.md b/changelog.d/unreleased/3493.fixed.md index e70c0d3543..f74bea4fe4 100644 --- a/changelog.d/unreleased/3493.fixed.md +++ b/changelog.d/unreleased/3493.fixed.md @@ -4,14 +4,16 @@ issues: - 3493 affected: - .dockerignore + - .github/workflows/release.yml - Dockerfile + - src/CodeIndex/CodeIndex.csproj - tests/CodeIndex.Tests/ReleaseWorkflowTests.cs --- ## English -- **Docker builds now use a narrower context (#3493)** — the build stage copies only restore, source, version, and license inputs needed for the runtime image, while `.dockerignore` excludes VCS metadata, tests, docs, workflow files, changelog fragments, tools, and agent artifacts from the build context. +- **Docker builds now use a narrower context (#3493)** — the build stage copies only restore, source, version, and license inputs needed for the runtime image, while `.dockerignore` excludes VCS metadata, tests, docs, workflow files, changelog fragments, tools, and agent artifacts from the build context. Release builds pass commit, date, and dirty metadata through explicit build args so the image keeps the same version provenance without shipping `.git/`. ## 日本語 -- **Docker build context を絞り込みました (#3493)** — build stage は runtime image に必要な restore / source / version / license 入力だけをコピーし、`.dockerignore` は VCS metadata、tests、docs、workflow files、changelog fragments、tools、agent artifacts を build context から除外します。 +- **Docker build context を絞り込みました (#3493)** — build stage は runtime image に必要な restore / source / version / license 入力だけをコピーし、`.dockerignore` は VCS metadata、tests、docs、workflow files、changelog fragments、tools、agent artifacts を build context から除外します。release build は commit/date/dirty metadata を明示的な build args で渡すため、`.git/` を含めなくても image の version provenance を維持します。 diff --git a/src/CodeIndex/CodeIndex.csproj b/src/CodeIndex/CodeIndex.csproj index 9271e1e931..3c39fc3389 100644 --- a/src/CodeIndex/CodeIndex.csproj +++ b/src/CodeIndex/CodeIndex.csproj @@ -89,6 +89,7 @@ --> - $(CdidxGitDateRaw) + $(CdidxBuildDateOverride) + $(CdidxGitDateRaw) $([System.DateTime]::UtcNow.ToString('yyyy-MM-dd')) - $(CdidxGitShaRaw) + $(CdidxBuildCommitOverride) + $(CdidxGitShaRaw) unknown - clean - dirty + $(CdidxBuildDirtyOverride) + clean + dirty unknown diff --git a/tests/CodeIndex.Tests/ReleaseWorkflowTests.cs b/tests/CodeIndex.Tests/ReleaseWorkflowTests.cs index 6253ae44a1..95eb5cd18e 100644 --- a/tests/CodeIndex.Tests/ReleaseWorkflowTests.cs +++ b/tests/CodeIndex.Tests/ReleaseWorkflowTests.cs @@ -480,6 +480,12 @@ public void ReleaseWorkflow_PublishesOfficialContainerImage() Assert.Contains("ghcr.io/widthdom/codeindex:${version}", workflow); Assert.Contains("ghcr.io/widthdom/codeindex:latest", workflow); Assert.Contains("tags: ${{ steps.image-tags.outputs.tags }}", workflow); + Assert.Contains("Extract container build metadata", workflow); + Assert.Contains("git rev-parse --short=7 HEAD", workflow); + Assert.Contains("git show -s --format=%cd --date=format:%Y-%m-%d HEAD", workflow); + Assert.Contains("CDIDX_BUILD_COMMIT=${{ steps.container-metadata.outputs.commit }}", workflow); + Assert.Contains("CDIDX_BUILD_DATE=${{ steps.container-metadata.outputs.date }}", workflow); + Assert.Contains("CDIDX_BUILD_DIRTY=${{ steps.container-metadata.outputs.dirty }}", workflow); Assert.Contains("*-*) ;;", workflow); Assert.Contains("docker buildx imagetools inspect mcr.microsoft.com/dotnet/:8.0-alpine", dockerfile); Assert.Contains("FROM mcr.microsoft.com/dotnet/sdk:8.0-alpine@sha256:d9f4f4a5d99a43799b500ee1365c370e3233822fbe7d43666715d9b5b5cda2ab AS build", dockerfile); @@ -493,6 +499,10 @@ public void ReleaseWorkflow_PublishesOfficialContainerImage() Assert.Contains("COPY Directory.Build.props nuget.config version.json ./", dockerfile); Assert.Contains("COPY src/CodeIndex/CodeIndex.csproj src/CodeIndex/packages.lock.json src/CodeIndex/", dockerfile); Assert.Contains("COPY src/CodeIndex/ src/CodeIndex/", dockerfile); + Assert.Contains("ARG CDIDX_BUILD_COMMIT=unknown", dockerfile); + Assert.Contains("-p:CdidxBuildCommitOverride=\"$CDIDX_BUILD_COMMIT\"", dockerfile); + Assert.Contains("-p:CdidxBuildDateOverride=\"$build_date\"", dockerfile); + Assert.Contains("-p:CdidxBuildDirtyOverride=\"$CDIDX_BUILD_DIRTY\"", dockerfile); Assert.Contains(".git/", dockerignore); Assert.Contains("tests/", dockerignore); Assert.Contains("tools/", dockerignore); @@ -504,6 +514,9 @@ public void ReleaseWorkflow_PublishesOfficialContainerImage() Assert.Contains("linux-musl-x64", dockerfile); Assert.Contains("linux-musl-arm64", dockerfile); Assert.Contains("ENTRYPOINT [\"cdidx\"]", dockerfile); + Assert.Contains("CdidxBuildCommitOverride", project); + Assert.Contains("CdidxBuildDateOverride", project); + Assert.Contains("CdidxBuildDirtyOverride", project); Assert.Contains("Microsoft.NET.ILLink.Tasks\" Version=\"8.", project); Assert.DoesNotContain("Microsoft.NET.ILLink.Tasks\" Version=\"10.", project); } From 44335dfd8c1f9914c9e6033285bf9d72fc55c467 Mon Sep 17 00:00:00 2001 From: Widthdom Date: Sat, 13 Jun 2026 23:55:28 +0900 Subject: [PATCH 4/4] Preserve Docker bind-mount writes after dropping privileges (#3492) --- Dockerfile | 14 +++++------ changelog.d/unreleased/3492.fixed.md | 5 ++-- scripts/docker-entrypoint.sh | 24 +++++++++++++++++++ tests/CodeIndex.Tests/ReleaseWorkflowTests.cs | 13 +++++++--- 4 files changed, 44 insertions(+), 12 deletions(-) create mode 100644 scripts/docker-entrypoint.sh diff --git a/Dockerfile b/Dockerfile index ba1416c5a6..8d9fef50d9 100644 --- a/Dockerfile +++ b/Dockerfile @@ -33,11 +33,13 @@ RUN case "$TARGETARCH" in \ FROM mcr.microsoft.com/dotnet/runtime-deps:8.0-alpine@sha256:7ec14bf41e70f3ca60f7b369b077636f642a0e6867caf28677d970e0abd9c6e6 AS runtime -RUN apk add --no-cache ca-certificates \ - && addgroup -S cdidx \ - && adduser -S -D -H -G cdidx -h /repo cdidx \ +COPY scripts/docker-entrypoint.sh /usr/local/bin/cdidx-entrypoint +RUN apk add --no-cache ca-certificates su-exec \ + && addgroup -S -g 10001 cdidx \ + && adduser -S -D -H -u 10001 -G cdidx -h /repo cdidx \ && mkdir -p /repo \ - && chown cdidx:cdidx /repo + && chown cdidx:cdidx /repo \ + && chmod 0755 /usr/local/bin/cdidx-entrypoint WORKDIR /repo COPY --from=build /out/ /usr/local/lib/cdidx/ @@ -45,7 +47,5 @@ COPY LICENSE COMMERCIAL_LICENSE.md INTEGRATION_POLICY.md TRADEMARKS.md /usr/loca COPY LICENSES/ /usr/local/lib/cdidx/LICENSES/ RUN ln -s /usr/local/lib/cdidx/cdidx /usr/local/bin/cdidx -USER cdidx:cdidx - -ENTRYPOINT ["cdidx"] +ENTRYPOINT ["/usr/local/bin/cdidx-entrypoint"] CMD ["--help"] diff --git a/changelog.d/unreleased/3492.fixed.md b/changelog.d/unreleased/3492.fixed.md index b5f4b09a27..6d09ade3c1 100644 --- a/changelog.d/unreleased/3492.fixed.md +++ b/changelog.d/unreleased/3492.fixed.md @@ -4,13 +4,14 @@ issues: - 3492 affected: - Dockerfile + - scripts/docker-entrypoint.sh - tests/CodeIndex.Tests/ReleaseWorkflowTests.cs --- ## English -- **Docker images now pin .NET base images and run as a non-root user (#3492)** — the container build uses explicit multi-arch MCR image digests and the runtime stage switches to a dedicated `cdidx` user after installation. +- **Docker images now pin .NET base images and drop privileges for `cdidx` (#3492)** — the container build uses explicit multi-arch MCR image digests, creates a dedicated `cdidx` UID, and starts through an entrypoint that runs `cdidx` as the `/repo` owner or the dedicated UID so bind-mounted repositories remain writable without keeping the CLI process as root. ## 日本語 -- **Docker image が .NET base image を digest 固定し、non-root user で実行されるようになりました (#3492)** — container build は MCR の multi-arch image digest を明示し、runtime stage はインストール後に専用の `cdidx` user へ切り替えます。 +- **Docker image が .NET base image を digest 固定し、`cdidx` 実行時に権限降格するようになりました (#3492)** — container build は MCR の multi-arch image digest を明示し、専用の `cdidx` UID を作成します。entrypoint は bind mount した repository を書き込めるよう、`/repo` の owner または専用 UID で `cdidx` を実行し、CLI process を root のままにしません。 diff --git a/scripts/docker-entrypoint.sh b/scripts/docker-entrypoint.sh new file mode 100644 index 0000000000..1ae5b76f36 --- /dev/null +++ b/scripts/docker-entrypoint.sh @@ -0,0 +1,24 @@ +#!/bin/sh +set -eu + +if [ "$(id -u)" -eq 0 ]; then + target_uid="${CDIDX_RUN_UID:-}" + target_gid="${CDIDX_RUN_GID:-}" + + if [ -z "$target_uid" ] && [ -e /repo ]; then + target_uid="$(stat -c '%u' /repo)" + fi + if [ -z "$target_gid" ] && [ -e /repo ]; then + target_gid="$(stat -c '%g' /repo)" + fi + + target_uid="${target_uid:-10001}" + target_gid="${target_gid:-10001}" + + if [ "$target_uid" != "0" ]; then + export HOME=/repo + exec su-exec "${target_uid}:${target_gid}" cdidx "$@" + fi +fi + +exec cdidx "$@" diff --git a/tests/CodeIndex.Tests/ReleaseWorkflowTests.cs b/tests/CodeIndex.Tests/ReleaseWorkflowTests.cs index 95eb5cd18e..df7115bf6a 100644 --- a/tests/CodeIndex.Tests/ReleaseWorkflowTests.cs +++ b/tests/CodeIndex.Tests/ReleaseWorkflowTests.cs @@ -468,6 +468,7 @@ public void ReleaseWorkflow_PublishesOfficialContainerImage() var workflow = File.ReadAllText(Path.Combine(root, ".github", "workflows", "release.yml")); var dockerfile = File.ReadAllText(Path.Combine(root, "Dockerfile")); var dockerignore = File.ReadAllText(Path.Combine(root, ".dockerignore")); + var entrypoint = File.ReadAllText(Path.Combine(root, "scripts", "docker-entrypoint.sh")); var project = File.ReadAllText(Path.Combine(root, "src", "CodeIndex", "CodeIndex.csproj")); Assert.Contains("publish-container:", workflow); @@ -492,9 +493,15 @@ public void ReleaseWorkflow_PublishesOfficialContainerImage() Assert.Contains("FROM mcr.microsoft.com/dotnet/runtime-deps:8.0-alpine@sha256:7ec14bf41e70f3ca60f7b369b077636f642a0e6867caf28677d970e0abd9c6e6 AS runtime", dockerfile); Assert.DoesNotContain("FROM mcr.microsoft.com/dotnet/sdk:8.0-alpine AS build", dockerfile); Assert.DoesNotContain("FROM mcr.microsoft.com/dotnet/runtime-deps:8.0-alpine AS runtime", dockerfile); - Assert.Contains("adduser -S -D -H -G cdidx -h /repo cdidx", dockerfile); + Assert.Contains("COPY scripts/docker-entrypoint.sh /usr/local/bin/cdidx-entrypoint", dockerfile); + Assert.Contains("apk add --no-cache ca-certificates su-exec", dockerfile); + Assert.Contains("addgroup -S -g 10001 cdidx", dockerfile); + Assert.Contains("adduser -S -D -H -u 10001 -G cdidx -h /repo cdidx", dockerfile); Assert.Contains("chown cdidx:cdidx /repo", dockerfile); - Assert.Contains("USER cdidx:cdidx", dockerfile); + Assert.DoesNotContain("USER cdidx:cdidx", dockerfile); + Assert.Contains("stat -c '%u' /repo", entrypoint); + Assert.Contains("stat -c '%g' /repo", entrypoint); + Assert.Contains("su-exec \"${target_uid}:${target_gid}\" cdidx \"$@\"", entrypoint); Assert.DoesNotContain("COPY . .", dockerfile); Assert.Contains("COPY Directory.Build.props nuget.config version.json ./", dockerfile); Assert.Contains("COPY src/CodeIndex/CodeIndex.csproj src/CodeIndex/packages.lock.json src/CodeIndex/", dockerfile); @@ -513,7 +520,7 @@ public void ReleaseWorkflow_PublishesOfficialContainerImage() Assert.Contains("ARG TARGETARCH=amd64", dockerfile); Assert.Contains("linux-musl-x64", dockerfile); Assert.Contains("linux-musl-arm64", dockerfile); - Assert.Contains("ENTRYPOINT [\"cdidx\"]", dockerfile); + Assert.Contains("ENTRYPOINT [\"/usr/local/bin/cdidx-entrypoint\"]", dockerfile); Assert.Contains("CdidxBuildCommitOverride", project); Assert.Contains("CdidxBuildDateOverride", project); Assert.Contains("CdidxBuildDirtyOverride", project);