From eda23afb7b027e71725a2ca865f0118ffcea4463 Mon Sep 17 00:00:00 2001 From: Neal Mummau Date: Mon, 20 Oct 2025 00:00:07 -0400 Subject: [PATCH 1/4] feat(build): container publish for image with dacpac and sqlpackage - paird with video demo: https://youtu.be/NaCNs0OOUbM - new build target PublishContainer will create a docker image that can be run to publish your project's dacpac to a target database via sqlpackage --- .gitignore | 3 +- src/MSBuild.Sdk.SqlProj/Sdk/Sdk.targets | 82 +++++++++++++++++++ .../tools/container/sqlpackage.Dockerfile | 46 +++++++++++ 3 files changed, 130 insertions(+), 1 deletion(-) create mode 100644 src/MSBuild.Sdk.SqlProj/tools/container/sqlpackage.Dockerfile diff --git a/.gitignore b/.gitignore index c96abe90..9f05de2b 100644 --- a/.gitignore +++ b/.gitignore @@ -340,7 +340,8 @@ ASALocalRun/ healthchecksdb # Exclude tools folder -src/MSBuild.Sdk.SqlProj/tools/ +src/MSBuild.Sdk.SqlProj/tools/* +!src/MSBuild.Sdk.SqlProj/tools/container/ # Exclude generated Dacpac's *.dacpac diff --git a/src/MSBuild.Sdk.SqlProj/Sdk/Sdk.targets b/src/MSBuild.Sdk.SqlProj/Sdk/Sdk.targets index 0b0eb508..8105d37d 100644 --- a/src/MSBuild.Sdk.SqlProj/Sdk/Sdk.targets +++ b/src/MSBuild.Sdk.SqlProj/Sdk/Sdk.targets @@ -310,4 +310,86 @@ + + + + + docker + + + $(MSBuildThisFileDirectory)..\tools\container\sqlpackage.Dockerfile + + + $([System.String]::Copy('$(MSBuildProjectName)').ToLowerInvariant().Trim())-publisher + $([System.String]::Copy('$(Configuration)').ToLowerInvariant().Trim()) + + + 170.2.70 + + + $(MSBuildProjectDirectory)\.container + $(MSBuildProjectName).dacpac + $(ContainerStagingDir)\$(ContainerDacpacName) + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/src/MSBuild.Sdk.SqlProj/tools/container/sqlpackage.Dockerfile b/src/MSBuild.Sdk.SqlProj/tools/container/sqlpackage.Dockerfile new file mode 100644 index 00000000..ef7db16b --- /dev/null +++ b/src/MSBuild.Sdk.SqlProj/tools/container/sqlpackage.Dockerfile @@ -0,0 +1,46 @@ +# Base image: ships with the SDK +ARG BASE_IMAGE=mcr.microsoft.com/dotnet/runtime:8.0 +FROM ${BASE_IMAGE} +ARG SQLPACKAGE_VERSION +ARG DACPAC_NAME +ENV DACPAC_NAME=${DACPAC_NAME} + +# Install sqlpackage dependencies and pull from NuGet +# - Supports native (platform zip) and dotnet tool layouts +# - Installs a stable wrapper in /usr/local/bin/sqlpackage +RUN set -eux; \ + apt-get update \ + && apt-get install -y --no-install-recommends ca-certificates curl unzip \ + && curl -L -o /tmp/sqlpackage.nupkg "https://globalcdn.nuget.org/packages/microsoft.sqlpackage.${SQLPACKAGE_VERSION}.nupkg" \ + && mkdir -p /opt/sqlpackage-src /opt/sqlpackage \ + && unzip -q /tmp/sqlpackage.nupkg -d /opt/sqlpackage-src \ + && inner="$(find /opt/sqlpackage-src -type f -iname '*linux-x64*.zip' | head -n1 || true)" \ + && if [ -n "$inner" ]; then \ + unzip -q "$inner" -d /opt/sqlpackage; \ + else \ + cp -R /opt/sqlpackage-src/tools/*/any/* /opt/sqlpackage/; \ + fi \ + && printf '#!/usr/bin/env bash\nset -euo pipefail\nif [ -x /opt/sqlpackage/sqlpackage ]; then exec /opt/sqlpackage/sqlpackage "$@"; elif [ -f /opt/sqlpackage/sqlpackage.dll ]; then exec dotnet /opt/sqlpackage/sqlpackage.dll "$@"; else echo "sqlpackage not found"; exit 127; fi\n' > /usr/local/bin/sqlpackage \ + && chmod +x /usr/local/bin/sqlpackage \ + && rm -rf /var/lib/apt/lists/* /tmp/* /opt/sqlpackage-src \ + # Create entrypoint wrapper as root: + # - Bakes in `-Action:Publish` and `-SourceFile:/work/$DACPAC_NAME` + # - Forwards user-supplied args to sqlpackage + && install -d /usr/local/bin \ + && printf '%s\n' \ + '#!/usr/bin/env bash' \ + 'set -euo pipefail' \ + 'exec sqlpackage -Action:Publish -SourceFile:/work/"${DACPAC_NAME}" "$@"' \ + | tee /usr/local/bin/docker-entrypoint >/dev/null \ + && chmod +x /usr/local/bin/docker-entrypoint + +# Staging: the SDK copies all dacpacs into /work at build time +WORKDIR /work +COPY ./.container/*.dacpac /work/ + +# Drop privileges to a non-root user +RUN useradd -m runner +USER runner + +# Exec-form ENTRYPOINT for proper signal handling and linter compliance +ENTRYPOINT ["/usr/local/bin/docker-entrypoint"] From 0da2f6317f692f236a144ad33c1309e847a3bbb2 Mon Sep 17 00:00:00 2001 From: Neal Mummau Date: Mon, 20 Oct 2025 08:32:28 -0400 Subject: [PATCH 2/4] refactor: remove nice to have targets/features --- src/MSBuild.Sdk.SqlProj/Sdk/Sdk.targets | 17 +---------------- 1 file changed, 1 insertion(+), 16 deletions(-) diff --git a/src/MSBuild.Sdk.SqlProj/Sdk/Sdk.targets b/src/MSBuild.Sdk.SqlProj/Sdk/Sdk.targets index 8105d37d..c646685b 100644 --- a/src/MSBuild.Sdk.SqlProj/Sdk/Sdk.targets +++ b/src/MSBuild.Sdk.SqlProj/Sdk/Sdk.targets @@ -313,9 +313,6 @@ - - docker - $(MSBuildThisFileDirectory)..\tools\container\sqlpackage.Dockerfile @@ -377,19 +374,7 @@ DependsOnTargets="PrepareContainerDacpac"> - - - - - - - \ No newline at end of file From 3ea4a94a2cddf587ee509ec275f37afc1978bf22 Mon Sep 17 00:00:00 2001 From: Neal Mummau Date: Mon, 20 Oct 2025 21:22:12 -0400 Subject: [PATCH 3/4] refactor(sdk): tie SqlPackageVersion to DacFxVersion - Move hardcoded DacFx version to Sdk.props - Reference $(DacFxVersion) in DacpacTool.csproj - Set SqlPackageVersion from DacFxVersion in Sdk.targets - Ensures SDK owns versioning and keeps DacFx and SqlPackage in sync --- src/DacpacTool/DacpacTool.csproj | 2 +- src/MSBuild.Sdk.SqlProj/Sdk/Sdk.props | 1 + src/MSBuild.Sdk.SqlProj/Sdk/Sdk.targets | 7 +++++-- 3 files changed, 7 insertions(+), 3 deletions(-) diff --git a/src/DacpacTool/DacpacTool.csproj b/src/DacpacTool/DacpacTool.csproj index c4469f50..4cef19f0 100644 --- a/src/DacpacTool/DacpacTool.csproj +++ b/src/DacpacTool/DacpacTool.csproj @@ -12,7 +12,7 @@ - + diff --git a/src/MSBuild.Sdk.SqlProj/Sdk/Sdk.props b/src/MSBuild.Sdk.SqlProj/Sdk/Sdk.props index 8e8635ee..88fd0b66 100644 --- a/src/MSBuild.Sdk.SqlProj/Sdk/Sdk.props +++ b/src/MSBuild.Sdk.SqlProj/Sdk/Sdk.props @@ -10,6 +10,7 @@ $(MSBuildAllProjects);$(MsBuildThisFileFullPath) Sql150 .dacpac + 170.2.70 diff --git a/src/MSBuild.Sdk.SqlProj/Sdk/Sdk.targets b/src/MSBuild.Sdk.SqlProj/Sdk/Sdk.targets index c646685b..e48b5328 100644 --- a/src/MSBuild.Sdk.SqlProj/Sdk/Sdk.targets +++ b/src/MSBuild.Sdk.SqlProj/Sdk/Sdk.targets @@ -321,8 +321,11 @@ $([System.String]::Copy('$(MSBuildProjectName)').ToLowerInvariant().Trim())-publisher $([System.String]::Copy('$(Configuration)').ToLowerInvariant().Trim()) - - 170.2.70 + + $(DacFxVersion) $(MSBuildProjectDirectory)\.container From 778a3c7cfcbcdf1c5e5d245aca45bd9e661b9332 Mon Sep 17 00:00:00 2001 From: Neal Mummau Date: Mon, 20 Oct 2025 21:55:32 -0400 Subject: [PATCH 4/4] refactor: hardcode DacFx version in DacpacTool.csproj MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit The previous change attempted to use the $(DacFxVersion) property from the SDK to set the Microsoft.SqlServer.DacFx package version in DacpacTool.csproj. This didn’t work because DacpacTool.csproj does not import the custom SDK and therefore has no knowledge of properties defined in Sdk.props. --- src/DacpacTool/DacpacTool.csproj | 2 +- src/MSBuild.Sdk.SqlProj/Sdk/Sdk.props | 2 +- src/MSBuild.Sdk.SqlProj/Sdk/Sdk.targets | 4 ++-- 3 files changed, 4 insertions(+), 4 deletions(-) diff --git a/src/DacpacTool/DacpacTool.csproj b/src/DacpacTool/DacpacTool.csproj index 4cef19f0..c4469f50 100644 --- a/src/DacpacTool/DacpacTool.csproj +++ b/src/DacpacTool/DacpacTool.csproj @@ -12,7 +12,7 @@ - + diff --git a/src/MSBuild.Sdk.SqlProj/Sdk/Sdk.props b/src/MSBuild.Sdk.SqlProj/Sdk/Sdk.props index 88fd0b66..4d6e7e7e 100644 --- a/src/MSBuild.Sdk.SqlProj/Sdk/Sdk.props +++ b/src/MSBuild.Sdk.SqlProj/Sdk/Sdk.props @@ -10,7 +10,7 @@ $(MSBuildAllProjects);$(MsBuildThisFileFullPath) Sql150 .dacpac - 170.2.70 + 170.2.70 diff --git a/src/MSBuild.Sdk.SqlProj/Sdk/Sdk.targets b/src/MSBuild.Sdk.SqlProj/Sdk/Sdk.targets index e48b5328..9b6b566e 100644 --- a/src/MSBuild.Sdk.SqlProj/Sdk/Sdk.targets +++ b/src/MSBuild.Sdk.SqlProj/Sdk/Sdk.targets @@ -323,9 +323,9 @@ - $(DacFxVersion) + $(SqlPackageVersion) $(MSBuildProjectDirectory)\.container