Repository navigation
2.0.0: SQL Server 2025, current runtimes, and tests that can fail - #39
Merged
Merged
Conversation
Templates no longer copy 36 PNGs into every user's project. Byte-identical copies are kept once. The dotnet-aspire task list and 'Update .NET SDK' screenshots are dropped: that task is gone and the template's task list now matches the dotnet one.
- SQL Server 2025 replaces Azure SQL Edge. State that the SQL Database project's Azure SQL Database target is the compatibility check. - Document the Apple Silicon support boundary: native app container, SQL Server under emulation, which Microsoft does not support. - Correct the base OS (Ubuntu 24.04 for .NET, Debian 13 for Python and Node.js), MSSQL_SA_PASSWORD, forwarded ports, and task numbers. - Add the Dev Container CLI path, image variants, and per-language connection notes (Aspire AddConnectionString, mssql-python, mssql). - Remove Azure Data Studio links, broken relative links, the duplicate Docker extension entry, and the ms-mssql.sql-database-projects id. - Reference screenshots by absolute URL under docs/images.
Replace the marketing text with what the templates are, the four templates and their image variants, how to apply them, the Apple Silicon support boundary, and how testing works. The overview diagram moves to docs/images.
Carlos stays the owner until the handover names a team.
Weekly updates for Dev Container Features, the SQL Server image in each docker-compose.yml, and GitHub Actions. The template Dockerfiles are left out because their FROM tag is a template option placeholder that Dependabot cannot resolve.
actions/checkout v7.0.1 and devcontainers/action v1.4.3. The PAT step is unchanged; replacing it is a handover item.
One build.sh/test.sh pair for all templates instead of eight copies. The in-container tests now check the engine, the seeded Library database, tool versions, the SQL project target, an Azure-incompatible fixture, re-publish, and a failing password, and they fail on any error. Adds static gates for shared files, forbidden content, and image tags. Removes the generated deploy script, the duplicate dotnet-aspire test, committed test build outputs, and the docker system prune helper.
- Engine is mcr.microsoft.com/mssql/server:2025-latest (Enterprise Developer), pinned linux/amd64, with a healthcheck; the app container waits for it to be healthy. - Base image dotnet:2 with 10.0-noble (default) and 8.0-noble variants. - sqlcmd from the go-sqlcmd v1.10.0 release, checked against its SHA-256; SqlPackage 170.5.76 as a .NET tool; no Ubuntu 20.04 apt repository. - postCreateCommand always builds the SQL Database project and publishes it, and exits non-zero naming the failed step. - Microsoft.Build.Sql 2.2.0; committed bin/ and obj/ removed for all templates and ignored. ModelCollation stays: without it the model becomes case-sensitive. - Removes container_name so two templates can run side by side.
- python: python:3 with 3.14-trixie (default) and 3.13-trixie; mssql-python 1.14.0 replaces pyodbc and the ODBC 17 driver, which had no arm64 build. - javascript-node: javascript-node:5 with 24-trixie (default) and 22-trixie. - Both get .NET 10 from the dotnet Feature instead of install-dotnet.sh (amd64 only), and docker-outside-of-docker with moby off, since moby has no trixie packages. - dotnet-aspire: Aspire CLI 13.5.3 and Aspire.ProjectTemplates replace the obsolete workload; the Update .NET SDK task is gone, so trusting the certificate is task 4. - azure-cli Feature no longer pinned to latest through pip. - The smoke test checks that sqlDatabaseProjects.dotnetSDK Location has an SDK, and gives every build its own workspace so no stack or path is reused.
- Paths filter covers the four templates and the shared test files; no continue-on-error; actions pinned by SHA; checkout without persisted credentials. - ubuntu-latest runs the full stack. ubuntu-24.04-arm builds the app container and runs the checks that need no SQL Server, since SQL Server has no arm64 image. - A static job lints the scripts and runs the shared-file, forbidden-content, and image-tag gates. - postCreateCommand.sh reports the failed step from a function trap (shellcheck clean).
test/gauntlet.sh runs every layer in order and fails on the first broken one: source state, lint, static gates, negative controls for each home-grown check, image tags, the sqlcmd checksum build failure, the template matrix (arm64, amd64, non-default variants, and the arm64 CI path), mutation, and supply-chain checks. test/mutants.sh applies seven plausible bugs to copies of the committed tree and requires the smoke test to fail with the check each one breaks, and proves that each mutant ran.
…ry mutant ran The Dev Container CLI read the rest of the list from stdin, so the first dry run ran one mutant and reported 1/1.
…S4 runs; build.sh shows db logs on failure SQL Server 2022 rejects MSSQL_PID=EnterpriseDeveloper (2025 only), so an image-only mutant stopped at the healthcheck and never reached the engine-version check.
- python and javascript-node list Microsoft's bookworm Azure CLI suite, the fallback Microsoft Learn documents for Debian releases without their own suite. The azure-cli Feature now installs Microsoft's 2.90.0 instead of Debian's 2.74.0. - Drop eg2.vscode-npm-script, which VS Code marks deprecated; add GitHub Copilot Chat next to GitHub Copilot, which is still current on the Marketplace. - The CI workflow grants pull-requests: read, which dorny/paths-filter needs to list the changed files on pull_request.
- Every native tool in the app container (sqlcmd, dotnet, the SqlPackage and Aspire launchers, Bicep, azd, Docker CLI, the mssql-python driver) must match the container architecture, so an emulated amd64 binary fails on arm64. - S16: with an object the Azure target rejects, postCreateCommand.sh stops at the build and publishes nothing, even with an older dacpac present. - S5 reads the dacpac state before any check builds the project again. - The Azure CLI must come from Microsoft's repository. - check-forbidden matches any case and spacing, removal commands fed by docker listings, and scripts whose first command isn't set -euo pipefail. - The gauntlet checks every extension id against the Marketplace and VS Code's deprecation list, simulates the paths filter for test and shared files, pins the arm64 half of check-tags, and removes images a run pulled that the templates don't use. checkExtension, which no smoke test could call, is gone. - Three new mutants: an amd64 sqlcmd on arm64, a build failure ignored with || true, and build outputs removed after up.
…w diagram The uppercase-host control now plants a fictional example-gated registry host. The overview diagram's file name no longer contains db-dev.
…r emulation - dotnet and dotnet-aspire set SSL_CERT_DIR, as the .NET guidance for Linux says, so 'dotnet dev-certs https --trust' exits 0 instead of reporting partial trust. - The Apple Silicon sections say SQL Server occasionally crashes while it starts under emulation (1 start in 37 in our tests) and what to do about it. - CODEOWNERS no longer points at a document outside the repo.
…tasks 2 and 4 - New test/test-utils/check-static.sh, run by CI's static job and the gauntlet: template ids, target platform, the Library .sql files against a hash manifest, LocalDev and port 1433, the compose shape, and CI integrity (known conditions only, no swallowed failures, the smoke step and its full mode, the action's exact commands, the Pick step executed, the static job's gates, the paths filter). - check-forbidden catches more spellings: ACR and podman logins, short credential names, unfiltered docker listings in $() or backticks, a line-continued prune, set +e, and Dockerfile steps that swallow a failure; non-ASCII file names are read as they are. - S12 builds the dotnet, python, and javascript-node Dockerfiles with a bad checksum. - The smoke tests run tasks 2 and 4 from tasks.json; a task 2 cwd mutant joins the list. - The extension deprecation check ignores case; the wheel hash check has a control. - Test labels use SPEC ids.
…is clean ShellCheck 0.11 locally accepts the A && B || C chain; the 0.9.0 that ubuntu-24.04 ships reports SC2015 and the lint step exits 123.
The workflow's paths filter uses an anchor, and Psych 4 (Ruby 3.1+, what ubuntu-24.04 ships) refuses aliases unless asked, so yaml2json raised and the S15 paths-filter simulation compared against nothing. Psych 3 rejects the keyword, so the call falls back.
…t ids
- VS Code writes connection settings verbatim, so ${env:MSSQL_SA_PASSWORD} reached the
container's Machine settings.json as an empty password and the LocalDev profile could
not connect. The profile now carries the same literal password as .devcontainer/.env,
which is what the extension's schema expects.
- github.copilot and github.copilot-chat are gone from all four templates. Current VS
Code ships GitHub Copilot Chat built in, so installing either fails and takes the rest
of the batch down with it.
- postCreateCommand builds with -nodeReuse:false, so no MSBuild worker is left holding
database/Library/bin/Debug/Library.dacpac when the user runs the build task.
- NOTES and README: password change touches both files, GitHub Copilot comes with VS
Code, the dacpac lock and what to do, and a troubleshooting section for networks that
block nuget, npm, or PyPI.
… install
- S18 connects with the profile's own server, user, and password, and rejects a
${...} placeholder; check-static requires the profile password to equal .env.
- The extensions layer installs every configured id with a real VS Code server, in one
batch as VS Code does, and requires each one on disk afterwards. Control: an id that
ships built in.
- New mutant: the profile password back to a placeholder.
A single code-server invocation with many --install-extension flags installs only the first and still exits 0.
VS Code does not expand ${env:...} in settings, so the profile's password
arrived empty and the saved connection prompted instead of connecting.
${containerEnv:MSSQL_SA_PASSWORD} is resolved by the dev container CLI from
the container's environment at create time, so the password keeps living only
in .devcontainer/.env.
Measured: containerEnv -> P@ssw0rd!, env -> empty, localEnv -> empty.
The notes still described the literal password and told readers to change it in two files. There is one place now: .devcontainer/.env.
Seen once on a native x64 GitHub Actions runner as well as 1 in 37 starts under emulation. Same symptom, same fix: rebuild the container.
This was referenced Sep 16, 2026
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Why
The four templates have not been touched since July 2024, and none of them builds today, on any
machine.
devcontainer upfails at the firstRUNline in the Dockerfile, before any SQL toolinginstalls: the pinned
1-base images carry an expired Yarn apt key, and thebullseyedefaults hitDebian 11's expired security repo. That happens on x64 and Arm alike, so every person who picked one
of these templates recently got a broken container. They are still being picked: about 12.9K GHCR
downloads across the four packages.
Behind that first failure sat a retired engine (Azure SQL Edge, out of support since 2025-09-30,
issue #29) and a CI pipeline that had never actually tested anything.
What changed
Engine. Azure SQL Edge is replaced by SQL Server 2025 (
mcr.microsoft.com/mssql/server:2025-latest,Enterprise Developer), pinned to
linux/amd64with a health check, and the app container now waitsfor the database to be healthy instead of polling in a shell loop. The SQL Database project still
targets Azure SQL Database (
SqlAzureV12DatabaseSchemaProvider); that build, not the local engine,is what guarantees compatibility, and a test proves it rejects an incompatible object (SQL70015).
Apple Silicon. The app container now runs natively on Arm. Previously three separate pieces were
x64-only: .NET from the Debian apt feed, ODBC Driver 17, and the SqlPackage zip. Today .NET comes
from the official dev container feature, SqlPackage is installed as a .NET tool, go-sqlcmd is
fetched per architecture with a pinned SHA-256, and Python uses Microsoft's
mssql-pythondriver,which needs no system ODBC at all. Every installed binary is checked against the container's
architecture. SQL Server itself is still x64 only and runs under emulation; the docs say so plainly.
Versions. .NET 10 (8.0 still offered until its 2026-11-10 end of support), Python 3.14, Node 24,
Microsoft.Build.Sql2.2.0, Aspire 13.5 through the Aspire CLI instead of the long-obsoleteworkload, Azure CLI 2.90, and current dev container features. Actions are pinned by SHA, and
Dependabot now watches features, compose images and actions.
A connection profile that actually connects. The shipped
LocalDevprofile used"password": "${env:MSSQL_SA_PASSWORD}". Nothing expands that in VS Code settings, so the savedprofile arrived with an empty password and prompted on first use, and had done since 1.2.2. It now
uses
${containerEnv:MSSQL_SA_PASSWORD}, which the dev container tooling resolves from thecontainer's environment at create time, so the password still lives only in
.devcontainer/.env.Tests that can fail. The old CI selected a template removed in 2024, called scripts that did not
exist, and was marked
continue-on-error. It is replaced by a smoke test per template onubuntu-latestandubuntu-24.04-arm, plus a static job that pins the things this PR promises:template ids, the Azure SQL Database target, the Library schema and seed, the connection profile and
port 1433, and the compose shape. Locally,
test/gauntlet.shruns the whole thing in one command.Repo hygiene. 125 committed build artifacts (10.5 MB) and 36 duplicated screenshots are gone;
the images live once under
docs/. The READMEs and NOTES were rewritten, including the Apple Siliconsupport boundary and a troubleshooting section for networks that block package registries.
Net: 252 files changed, +2,954 / −13,988.
Evidence
test/gauntlet.shpasses: 15 configurations (4 templates on Arm, the same 4 with the app forced tox64, 3 non-default variants, plus CI rehearsals), 486 checks, 12 of 12 planted defects caught,
42 negative controls, and a supply-chain layer with 0 known vulnerabilities.
profile resolved and connected, and tasks 2, 3 and 4 run from
tasks.json.Known limits
once on a native x64 CI runner. A rebuild fixes it. Documented in each template's notes.
or support.
dbservice is capped at 2 GB, exactly SQL Server's documented minimum. Whether thatcontributes to the startup crashes is unmeasured.
Follow-ups (not in this PR)
local-dev-experience-dev-containersand itsquickstart), and the screenshots there predate this release.
release.yamlstill depends on a personalPATsecret.