diff --git a/Directory.Build.props b/Directory.Build.props index d0caaa21ff..832f672a72 100644 --- a/Directory.Build.props +++ b/Directory.Build.props @@ -9,6 +9,13 @@ true $(MSBuildThisFileDirectory) + + bin\sts2-sqlite\ + obj\sts2-sqlite\ + $(DefaultItemExcludes);bin\**;obj\** + net10.0 net8.0 diff --git a/docs/sts2/CLIENT.md b/docs/sts2/CLIENT.md index 9cede78ce6..3a89707885 100644 --- a/docs/sts2/CLIENT.md +++ b/docs/sts2/CLIENT.md @@ -29,6 +29,10 @@ table and `docs/sts2/TRACE-SCHEMA.md` for the envelope/trace format. This is a copy-pasteable reference; it is documented rather than run in CI (no Node toolchain is assumed in this repo). The wire shapes match `docs/sts2/CONTRACT.md`. +The sample uses the non-production SQLite test driver. Production/default publishes +contain only the SQL Client driver; build locally with +`-p:IncludeSts2SqliteDriver=true` to make the `sqlite` driver available. + ```typescript import { spawn } from "node:child_process"; import { diff --git a/docs/sts2/COMPONENTS.md b/docs/sts2/COMPONENTS.md index 6ee62a4ada..1fac2e88dc 100644 --- a/docs/sts2/COMPONENTS.md +++ b/docs/sts2/COMPONENTS.md @@ -1,7 +1,7 @@ # STS2 Components -Roles come from each project's COMPONENT.md; references are read from the csproj files on disk. The allowed matrix is SPEC §4; DependencyMatrixTests enforces it (I11). +Roles come from each project's COMPONENT.md; references are read from the csproj files on disk. Conditional references include their MSBuild condition. The allowed matrix is SPEC §4; DependencyMatrixTests enforces it (I11). ## Microsoft.SqlTools.Sts2.Abstractions @@ -14,7 +14,7 @@ Driver port (IDbDriver/IDbSession), clock and id abstractions. Composition root invoked by legacy Program.cs; owns --enable-sts2 / STS_ENABLE_STS2 activation and process wiring. -- Project references: Microsoft.SqlTools.Sts2.Contracts, Microsoft.SqlTools.Sts2.Drivers.SqlClient, Microsoft.SqlTools.Sts2.Drivers.Sqlite, Microsoft.SqlTools.Sts2.Hosting, Microsoft.SqlTools.Sts2.Multiplexer, Microsoft.SqlTools.Sts2.Runtime +- Project references: Microsoft.SqlTools.Sts2.Contracts, Microsoft.SqlTools.Sts2.Drivers.SqlClient, Microsoft.SqlTools.Sts2.Drivers.Sqlite (condition: `'$(IncludeSts2SqliteDriver)' == 'true'`), Microsoft.SqlTools.Sts2.Hosting, Microsoft.SqlTools.Sts2.Multiplexer, Microsoft.SqlTools.Sts2.Runtime - Package references: none (BCL only) ## Microsoft.SqlTools.Sts2.Contracts @@ -40,7 +40,7 @@ Production driver adapter over Microsoft.Data.SqlClient. ## Microsoft.SqlTools.Sts2.Drivers.Sqlite -Portable real-I/O driver adapter over Microsoft.Data.Sqlite. +Non-production real-I/O test driver adapter over Microsoft.Data.Sqlite. - Project references: Microsoft.SqlTools.Sts2.Abstractions, Microsoft.SqlTools.Sts2.Contracts - Package references: Microsoft.Data.Sqlite, SQLitePCLRaw.lib.e_sqlite3 diff --git a/src/sts2/Microsoft.SqlTools.Sts2.Bootstrap/COMPONENT.md b/src/sts2/Microsoft.SqlTools.Sts2.Bootstrap/COMPONENT.md index eac3ec1230..7c0da69507 100644 --- a/src/sts2/Microsoft.SqlTools.Sts2.Bootstrap/COMPONENT.md +++ b/src/sts2/Microsoft.SqlTools.Sts2.Bootstrap/COMPONENT.md @@ -2,8 +2,10 @@ **Role:** Composition root invoked by legacy Program.cs; owns --enable-sts2 / STS_ENABLE_STS2 activation and process wiring. -**Allowed dependencies:** Hosting, Runtime, Multiplexer, Drivers.SqlClient, Drivers.Sqlite, Contracts +**Allowed dependencies:** Hosting, Runtime, Multiplexer, Drivers.SqlClient, Contracts; Drivers.Sqlite only when `IncludeSts2SqliteDriver=true` **Forbidden:** legacy namespaces See docs/sts2/SPEC.md SS4 for the authoritative dependency matrix. + +The default and production publish graph excludes Drivers.Sqlite. Local and E2E builds may opt in with `-p:IncludeSts2SqliteDriver=true`. diff --git a/src/sts2/Microsoft.SqlTools.Sts2.Bootstrap/Microsoft.SqlTools.Sts2.Bootstrap.csproj b/src/sts2/Microsoft.SqlTools.Sts2.Bootstrap/Microsoft.SqlTools.Sts2.Bootstrap.csproj index 8c9204b926..71477a17fa 100644 --- a/src/sts2/Microsoft.SqlTools.Sts2.Bootstrap/Microsoft.SqlTools.Sts2.Bootstrap.csproj +++ b/src/sts2/Microsoft.SqlTools.Sts2.Bootstrap/Microsoft.SqlTools.Sts2.Bootstrap.csproj @@ -1,13 +1,18 @@ STS2 composition root called by legacy Program.cs. Owns activation flags and process-level wiring. + + false + $(DefineConstants);STS2_INCLUDE_SQLITE_DRIVER - + diff --git a/src/sts2/Microsoft.SqlTools.Sts2.Bootstrap/Sts2Bootstrap.cs b/src/sts2/Microsoft.SqlTools.Sts2.Bootstrap/Sts2Bootstrap.cs index 4b63cf9ed6..19bba7f47d 100644 --- a/src/sts2/Microsoft.SqlTools.Sts2.Bootstrap/Sts2Bootstrap.cs +++ b/src/sts2/Microsoft.SqlTools.Sts2.Bootstrap/Sts2Bootstrap.cs @@ -10,7 +10,9 @@ using System.Text; using System.Threading.Tasks; using Microsoft.SqlTools.Sts2.Abstractions; +#if STS2_INCLUDE_SQLITE_DRIVER using Microsoft.SqlTools.Sts2.Drivers.Sqlite; +#endif using Microsoft.SqlTools.Sts2.Hosting; using Microsoft.SqlTools.Sts2.Multiplexer; @@ -118,11 +120,7 @@ public static Sts2BootstrapHandle TryStart(string[] args, string? logFilePath) // never conflate this run with an earlier one in the shared log directory. JournalDirectory = Path.Combine(logDirectory, "sts2", runId), ServiceVersion = typeof(Sts2Bootstrap).Assembly.GetName().Version?.ToString() ?? "0.0.0.0", - Drivers = new Dictionary - { - ["sqlclient"] = new Drivers.SqlClient.SqlClientDriver(), // production (M5) - ["sqlite"] = new SqliteDriver(), // portable (M4) - }, + Drivers = CreateDrivers(), CommandLine = SanitizeCommandLine(args), }, multiplexer.Sts2OutputWriter, @@ -138,6 +136,20 @@ public static Sts2BootstrapHandle TryStart(string[] args, string? logFilePath) return new Sts2BootstrapHandle(multiplexer.LegacyInput, multiplexer.LegacyOutput, multiplexer, session, diagnosticsLog); } + private static Dictionary CreateDrivers() + { + var drivers = new Dictionary + { + ["sqlclient"] = new Drivers.SqlClient.SqlClientDriver(), // production (M5) + }; +#if STS2_INCLUDE_SQLITE_DRIVER + // Non-production real-I/O test driver. The conditional project reference + // keeps Microsoft.Data.Sqlite and every native SQLite RID out of release builds. + drivers["sqlite"] = new SqliteDriver(); +#endif + return drivers; + } + /// /// Records the command line for the journal manifest by SHAPE, not by blocklist /// (R033): flag NAMES are kept verbatim (they aren't secret and are useful forensics), diff --git a/src/sts2/Microsoft.SqlTools.Sts2.Drivers.Sqlite/COMPONENT.md b/src/sts2/Microsoft.SqlTools.Sts2.Drivers.Sqlite/COMPONENT.md index f5b1826d9d..b5ab8fe305 100644 --- a/src/sts2/Microsoft.SqlTools.Sts2.Drivers.Sqlite/COMPONENT.md +++ b/src/sts2/Microsoft.SqlTools.Sts2.Drivers.Sqlite/COMPONENT.md @@ -1,9 +1,11 @@ # Microsoft.SqlTools.Sts2.Drivers.Sqlite -**Role:** Portable real-I/O driver adapter over Microsoft.Data.Sqlite. +**Role:** Non-production real-I/O test driver adapter over Microsoft.Data.Sqlite. **Allowed dependencies:** Abstractions, Contracts, Microsoft.Data.Sqlite, SQLitePCLRaw.lib.e_sqlite3 **Forbidden:** Core, Runtime, Hosting, legacy namespaces The enforced dependency matrix is in [DependencyMatrixTests](../../../test/sts2/Microsoft.SqlTools.Sts2.UnitTests/Architecture/DependencyMatrixTests.cs) (I11); see the generated [component graph](../../../docs/sts2/COMPONENTS.md) for the references on disk. + +This project is tested directly. It enters the ServiceLayer graph only when a local or test build sets `IncludeSts2SqliteDriver=true`. diff --git a/src/sts2/Microsoft.SqlTools.Sts2.Drivers.Sqlite/Microsoft.SqlTools.Sts2.Drivers.Sqlite.csproj b/src/sts2/Microsoft.SqlTools.Sts2.Drivers.Sqlite/Microsoft.SqlTools.Sts2.Drivers.Sqlite.csproj index 6f159de3cf..75910e2667 100644 --- a/src/sts2/Microsoft.SqlTools.Sts2.Drivers.Sqlite/Microsoft.SqlTools.Sts2.Drivers.Sqlite.csproj +++ b/src/sts2/Microsoft.SqlTools.Sts2.Drivers.Sqlite/Microsoft.SqlTools.Sts2.Drivers.Sqlite.csproj @@ -1,6 +1,6 @@ - STS2 portable real-I/O driver adapter over Microsoft.Data.Sqlite. Quick-CI honesty check for the port. + STS2 non-production real-I/O test driver over Microsoft.Data.Sqlite. Quick-CI honesty check for the port. @@ -8,7 +8,7 @@ + every opted-in consumer and the direct test references. Version in Packages.props. --> diff --git a/src/sts2/Microsoft.SqlTools.Sts2.Testing/GeneratedDocs.cs b/src/sts2/Microsoft.SqlTools.Sts2.Testing/GeneratedDocs.cs index d0d7e7fc60..6159a131e8 100644 --- a/src/sts2/Microsoft.SqlTools.Sts2.Testing/GeneratedDocs.cs +++ b/src/sts2/Microsoft.SqlTools.Sts2.Testing/GeneratedDocs.cs @@ -230,7 +230,7 @@ public static string Components(string sts2SourceDir) { var sb = new StringBuilder(Header); sb.Append("# STS2 Components\n\n"); - sb.Append("Roles come from each project's COMPONENT.md; references are read from the csproj files on disk. The allowed matrix is SPEC §4; DependencyMatrixTests enforces it (I11).\n\n"); + sb.Append("Roles come from each project's COMPONENT.md; references are read from the csproj files on disk. Conditional references include their MSBuild condition. The allowed matrix is SPEC §4; DependencyMatrixTests enforces it (I11).\n\n"); foreach (string projectDir in Directory.EnumerateDirectories(sts2SourceDir).Order(StringComparer.Ordinal)) { @@ -252,7 +252,14 @@ public static string Components(string sts2SourceDir) XDocument csproj = XDocument.Load(csprojPath); string[] projectRefs = csproj.Descendants("ProjectReference") - .Select(r => Path.GetFileNameWithoutExtension(r.Attribute("Include")!.Value.Replace('\\', '/'))) + .Select(r => + { + string name = Path.GetFileNameWithoutExtension(r.Attribute("Include")!.Value.Replace('\\', '/')); + string? condition = r.Attribute("Condition")?.Value; + return string.IsNullOrWhiteSpace(condition) + ? name + : $"{name} (condition: `{condition}`)"; + }) .Order(StringComparer.Ordinal).ToArray(); string[] packageRefs = csproj.Descendants("PackageReference") .Select(r => (r.Attribute("Include") ?? r.Attribute("Update"))!.Value) diff --git a/test/sts2/Microsoft.SqlTools.Sts2.E2ETests/Microsoft.SqlTools.Sts2.E2ETests.csproj b/test/sts2/Microsoft.SqlTools.Sts2.E2ETests/Microsoft.SqlTools.Sts2.E2ETests.csproj index a0b9bfd28d..af34cf3df6 100644 --- a/test/sts2/Microsoft.SqlTools.Sts2.E2ETests/Microsoft.SqlTools.Sts2.E2ETests.csproj +++ b/test/sts2/Microsoft.SqlTools.Sts2.E2ETests/Microsoft.SqlTools.Sts2.E2ETests.csproj @@ -10,10 +10,31 @@ + + + $(MSBuildThisFileDirectory)..\..\..\src\Microsoft.SqlTools.ServiceLayer\Microsoft.SqlTools.ServiceLayer.csproj + Configuration=$(Configuration);IncludeSts2SqliteDriver=true;Sts2IsolatedSqliteBuild=true + - - + + + + + diff --git a/test/sts2/Microsoft.SqlTools.Sts2.E2ETests/ServiceProcessClient.cs b/test/sts2/Microsoft.SqlTools.Sts2.E2ETests/ServiceProcessClient.cs index 8418e745a4..f118e534ff 100644 --- a/test/sts2/Microsoft.SqlTools.Sts2.E2ETests/ServiceProcessClient.cs +++ b/test/sts2/Microsoft.SqlTools.Sts2.E2ETests/ServiceProcessClient.cs @@ -45,7 +45,7 @@ private ServiceProcessClient(Process process) /// Queue of notifications received from the service, by method name. public ConcurrentQueue<(string Method, JsonElement Params)> Notifications { get; } = new(); - public static string LocateServiceDll() + public static string LocateServiceDll(bool includeSqlite = false) { string? dir = AppContext.BaseDirectory; while (dir != null && !File.Exists(Path.Combine(dir, "sqltoolsservice.sln"))) @@ -60,11 +60,17 @@ public static string LocateServiceDll() DirectoryInfo testOutputDirectory = new(AppContext.BaseDirectory); string currentTfm = testOutputDirectory.Name; string currentConfiguration = testOutputDirectory.Parent?.Name ?? "Debug"; - string candidate = Path.Combine( + string serviceOutputRoot = Path.Combine( dir, "src", "Microsoft.SqlTools.ServiceLayer", - "bin", + "bin"); + if (includeSqlite) + { + serviceOutputRoot = Path.Combine(serviceOutputRoot, "sts2-sqlite"); + } + string candidate = Path.Combine( + serviceOutputRoot, currentConfiguration, currentTfm, "MicrosoftSqlToolsServiceLayer.dll"); @@ -77,10 +83,10 @@ public static string LocateServiceDll() ". The E2E project reference must build ServiceLayer for the current configuration and TFM."); } - public static ServiceProcessClient Start(bool enableSts2, string logDirectory) + public static ServiceProcessClient Start(bool enableSts2, string logDirectory, bool includeSqlite = false) { Directory.CreateDirectory(logDirectory); - string args = "\"" + LocateServiceDll() + "\" --log-file \"" + Path.Combine(logDirectory, "sqltools.log") + "\""; + string args = "\"" + LocateServiceDll(includeSqlite) + "\" --log-file \"" + Path.Combine(logDirectory, "sqltools.log") + "\""; if (enableSts2) { args += " --enable-sts2"; diff --git a/test/sts2/Microsoft.SqlTools.Sts2.E2ETests/StdioE2ETests.cs b/test/sts2/Microsoft.SqlTools.Sts2.E2ETests/StdioE2ETests.cs index e753b27805..9e4d07ca51 100644 --- a/test/sts2/Microsoft.SqlTools.Sts2.E2ETests/StdioE2ETests.cs +++ b/test/sts2/Microsoft.SqlTools.Sts2.E2ETests/StdioE2ETests.cs @@ -56,6 +56,13 @@ public void SpawnedSubjectMatchesCurrentTestBuild() Assert.True(File.Exists(serviceDll)); } + [Fact] + public void DefaultSubjectExcludesSqliteAssets() + { + string serviceDirectory = Path.GetDirectoryName(ServiceProcessClient.LocateServiceDll())!; + Assert.Empty(Directory.EnumerateFiles(serviceDirectory, "*sqlite*", SearchOption.AllDirectories)); + } + [Fact] public async Task DisabledMode_V1VersionWorks_AndNoSts2ArtifactsAreCreated() { @@ -119,7 +126,10 @@ public async Task EnabledMode_InitializeWorksAndJournalIsWritten() [Fact] public async Task EnabledMode_SqliteQueryExercisesPagedLifecycleOverRealStdio() { - await using var client = ServiceProcessClient.Start(enableSts2: true, logDirectory: logDirectory); + await using var client = ServiceProcessClient.Start( + enableSts2: true, + logDirectory: logDirectory, + includeSqlite: true); await client.RequestAsync("v2/initialize", new { clientName = "e2e" }, TestTimeout); JsonElement open = await client.RequestAsync("v2/connection.open", diff --git a/test/sts2/Microsoft.SqlTools.Sts2.UnitTests/Architecture/DependencyMatrixTests.cs b/test/sts2/Microsoft.SqlTools.Sts2.UnitTests/Architecture/DependencyMatrixTests.cs index 226fdefe74..4b453b1c9c 100644 --- a/test/sts2/Microsoft.SqlTools.Sts2.UnitTests/Architecture/DependencyMatrixTests.cs +++ b/test/sts2/Microsoft.SqlTools.Sts2.UnitTests/Architecture/DependencyMatrixTests.cs @@ -97,6 +97,31 @@ public void AllMatrixProjectsExistOnDisk() } } + [Fact] + public void SqliteIsOptInAtProductCompositionRoot() + { + string bootstrapProjectPath = Path.Combine( + RepoRoot.Sts2SourceDir, + "Microsoft.SqlTools.Sts2.Bootstrap", + "Microsoft.SqlTools.Sts2.Bootstrap.csproj"); + XDocument bootstrapProject = XDocument.Load(bootstrapProjectPath); + + XElement includeProperty = Assert.Single( + bootstrapProject.Descendants("IncludeSts2SqliteDriver")); + Assert.Equal("false", includeProperty.Value); + Assert.Contains("IncludeSts2SqliteDriver", includeProperty.Attribute("Condition")?.Value); + + XElement sqliteReference = Assert.Single( + bootstrapProject.Descendants("ProjectReference").Where(reference => + string.Equals( + Path.GetFileNameWithoutExtension(reference.Attribute("Include")!.Value.Replace('\\', '/')), + "Microsoft.SqlTools.Sts2.Drivers.Sqlite", + StringComparison.OrdinalIgnoreCase))); + string? condition = sqliteReference.Attribute("Condition")?.Value; + Assert.Contains("IncludeSts2SqliteDriver", condition); + Assert.Contains("true", condition); + } + [Fact] public void Sts2SourcesNeverUseLegacyServiceLayerNamespaces() {