Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
12 changes: 11 additions & 1 deletion backend/Dockerfile.api
Original file line number Diff line number Diff line change
Expand Up @@ -27,7 +27,17 @@ COPY . .
# (A csproj-only restore-cache split was attempted but reverted — the API's build-only ProjectReference to
# CodeSpace.Mcp breaks a manifest-only restore; not worth the build-speed micro-optimization.)
RUN dotnet restore CodeSpace.sln
RUN dotnet publish src/CodeSpace.Api/CodeSpace.Api.csproj -c Release -o /app --no-restore
# The commit this image is built from, stamped into AssemblyInformationalVersion so BuildIdentity can
# report it on every log line and on the boot banner. A deployment incident is not diagnosable until
# a pod can say which code it is running; a bare "1.0.0" cannot.
#
# The arg is for pipelines that know the sha (CI passes it); the git fallback covers every other build,
# because the whole source tree including .git is already in the context. "unknown" is the honest last
# resort rather than a version that looks meaningful and is not.
ARG SOURCE_REVISION_ID=""
RUN SHA="${SOURCE_REVISION_ID:-$(git rev-parse HEAD 2>/dev/null || echo unknown)}" \
&& echo "Building from revision $SHA" \
&& dotnet publish src/CodeSpace.Api/CodeSpace.Api.csproj -c Release -o /app --no-restore -p:SourceRevisionId="$SHA"

FROM mcr.microsoft.com/dotnet/aspnet:10.0 AS runtime

Expand Down
12 changes: 11 additions & 1 deletion backend/Dockerfile.worker
Original file line number Diff line number Diff line change
Expand Up @@ -46,7 +46,17 @@ FROM mcr.microsoft.com/dotnet/sdk:10.0 AS build
WORKDIR /src
COPY . .
RUN dotnet restore CodeSpace.sln
RUN dotnet publish src/CodeSpace.Api/CodeSpace.Api.csproj -c Release -o /app --no-restore
# The commit this image is built from, stamped into AssemblyInformationalVersion so BuildIdentity can
# report it on every log line and on the boot banner. A deployment incident is not diagnosable until
# a pod can say which code it is running; a bare "1.0.0" cannot.
#
# The arg is for pipelines that know the sha (CI passes it); the git fallback covers every other build,
# because the whole source tree including .git is already in the context. "unknown" is the honest last
# resort rather than a version that looks meaningful and is not.
ARG SOURCE_REVISION_ID=""
RUN SHA="${SOURCE_REVISION_ID:-$(git rev-parse HEAD 2>/dev/null || echo unknown)}" \
&& echo "Building from revision $SHA" \
&& dotnet publish src/CodeSpace.Api/CodeSpace.Api.csproj -c Release -o /app --no-restore -p:SourceRevisionId="$SHA"

# ── 2. The Node-based harness CLIs, version-pinned (override at build time with --build-arg) ──
# These two ARG defaults are the SINGLE SOURCE OF TRUTH for the pinned CLI versions. A test
Expand Down
6 changes: 5 additions & 1 deletion backend/src/CodeSpace.Core/Settings/Logging/BuildIdentity.cs
Original file line number Diff line number Diff line change
Expand Up @@ -25,7 +25,11 @@ public static class BuildIdentity

private static string Resolve()
{
var assembly = Assembly.GetEntryAssembly() ?? typeof(BuildIdentity).Assembly;
// This assembly, deliberately, not Assembly.GetEntryAssembly(). The entry assembly is whatever
// launched the process, which under `dotnet test` is the VSTest host — it reported "17.12.0",
// a version belonging to Microsoft's tooling. Every host that matters ships this assembly, and
// it is the one built from this repository.
var assembly = typeof(BuildIdentity).Assembly;

var informational = assembly.GetCustomAttribute<AssemblyInformationalVersionAttribute>()?.InformationalVersion;

Expand Down
39 changes: 39 additions & 0 deletions backend/tests/CodeSpace.UnitTests/Settings/BuildIdentityTests.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,39 @@
using CodeSpace.Core.Settings.Logging;
using Shouldly;

namespace CodeSpace.UnitTests.Settings;

/// <summary>
/// The value every log line carries as <c>Build</c>, and the one the boot banner prints.
///
/// <para>It exists to answer the first question of any deployment incident — is this pod running the
/// code I think it is — so the only failure that matters is it being unable to distinguish two
/// builds. A bare <c>1.0.0</c> does exactly that, and is what a deployed image reported until the
/// Dockerfiles began passing <c>SourceRevisionId</c>.</para>
/// </summary>
[Trait("Category", "Unit")]
public class BuildIdentityTests
{
[Fact]
public void It_reports_something()
{
BuildIdentity.Value.ShouldNotBeNullOrWhiteSpace();
}

/// <summary>
/// Under <c>dotnet test</c> the SDK stamps the revision from the local checkout, so the sha is
/// present here exactly as it must be in a published image. This fails if the SDK ever stops
/// appending it — the silent version of the deployed <c>1.0.0</c>.
/// </summary>
[Fact]
public void It_carries_the_source_revision()
{
BuildIdentity.Value.ShouldContain("+",
customMessage: $"Build identity is '{BuildIdentity.Value}' — a version with no commit sha cannot tell two " +
"deployments apart, which is the only thing it is for. Publishes must pass " +
"-p:SourceRevisionId (see backend/Dockerfile.api).");

BuildIdentity.Value.Split('+')[^1].Length.ShouldBeGreaterThanOrEqualTo(7,
customMessage: "The suffix after '+' must be a commit sha long enough to identify a commit.");
}
}
Loading