Skip to content

Commit 3bb8cc4

Browse files
committed
add package validation
1 parent 83da2d4 commit 3bb8cc4

8 files changed

Lines changed: 271 additions & 15 deletions

File tree

.github/aw/actions-lock.json

Lines changed: 14 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,14 @@
1+
{
2+
"entries": {
3+
"actions/github-script@v8": {
4+
"repo": "actions/github-script",
5+
"version": "v8",
6+
"sha": "ed597411d8f924073f98dfc5c65a23a2325f34cd"
7+
},
8+
"github/gh-aw/actions/setup@v0.55.0": {
9+
"repo": "github/gh-aw/actions/setup",
10+
"version": "v0.55.0",
11+
"sha": "e211c855a20aa6cf9297b411466e1c382a8686db"
12+
}
13+
}
14+
}

build/build.fs

Lines changed: 89 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -299,6 +299,93 @@ Target.create "RunSample" (fun _ ->
299299
runImmediate
300300
})
301301

302+
// --------------------------------------------------------------------------------------
303+
// Pack the NuGet package using dotnet pack (cross-platform)
304+
305+
let nupkgDir = makeRootPath "temp/nupkg"
306+
let runtimeProjectPath = makeRootPath "src/SqlClient/SqlClient.fsproj"
307+
308+
Target.create "Pack" (fun _ ->
309+
Shell.cleanDir nupkgDir
310+
311+
DotNet.pack
312+
(fun args ->
313+
{ args with
314+
Configuration = DotNet.Release
315+
NoBuild = true
316+
OutputPath = Some nupkgDir
317+
MSBuildParams =
318+
{ args.MSBuildParams with
319+
Properties = [ ("Version", release.NugetVersion) ]
320+
DisableInternalBinLog = true } })
321+
runtimeProjectPath
322+
323+
Trace.logfn "Package created in %s" nupkgDir
324+
325+
for pkg in Directory.GetFiles(nupkgDir, "*.nupkg") do
326+
Trace.logfn " %s" (Path.GetFileName pkg))
327+
328+
// --------------------------------------------------------------------------------------
329+
// Validate that the NuGet package is consumable via PackageReference
330+
//
331+
// This builds and runs a standalone project that references the package from a
332+
// local NuGet source (temp/nupkg), exercising all three type providers end-to-end.
333+
// It proves the .nupkg layout is correct independently of the repo build output.
334+
335+
let validationProjectPath =
336+
makeRootPath "tests/PackageValidation/PackageValidation.fsproj"
337+
338+
Target.create "ValidatePackage" (fun _ ->
339+
340+
// If the CI connection-string override is set, also patch the validation app.config
341+
let connStrOverride =
342+
System.Environment.GetEnvironmentVariable "GITHUB_ACTION_SQL_SERVER_CONNECTION_STRING"
343+
344+
if not (String.IsNullOrWhiteSpace connStrOverride) then
345+
let appConfigPath = makeRootPath "tests/PackageValidation/app.config"
346+
let doc = XDocument.Load(appConfigPath)
347+
348+
let el =
349+
doc.Root.Element("connectionStrings").Elements("add")
350+
|> Seq.find (fun el -> el.Attribute(XName.Get "name").Value = "AdventureWorks")
351+
352+
el.SetAttributeValue(XName.Get "connectionString", connStrOverride)
353+
doc.Save(appConfigPath)
354+
355+
pipeline "ValidatePackage" {
356+
stage "clean validation caches" {
357+
run (fun _ ->
358+
// Clear any cached restore so we pick up the freshly-built nupkg
359+
let objDir = makeRootPath "tests/PackageValidation/obj"
360+
let binDir = makeRootPath "tests/PackageValidation/bin"
361+
362+
if Directory.Exists objDir then
363+
Directory.Delete(objDir, true)
364+
365+
if Directory.Exists binDir then
366+
Directory.Delete(binDir, true))
367+
}
368+
369+
stage "build validation project" {
370+
run
371+
$"dotnet build {validationProjectPath} -c Release -p:FSharpDataSqlClientVersion={release.NugetVersion} --tl"
372+
}
373+
374+
stage "run validation project" {
375+
run (fun _ ->
376+
let result =
377+
DotNet.exec
378+
id
379+
"run"
380+
$"--project {validationProjectPath} -c Release --no-build -p:FSharpDataSqlClientVersion={release.NugetVersion}"
381+
382+
if not result.OK then
383+
failwith "Package validation failed – the NuGet package is not consumable via PackageReference")
384+
}
385+
386+
runImmediate
387+
})
388+
302389
let funBuildRestore stageName sln =
303390
stage $"dotnet restore %s{stageName} '{sln}'" { run $"dotnet restore {sln} --tl" }
304391

@@ -415,8 +502,10 @@ open Fake.Core.TargetOperators // for ==>
415502
==> "CheckFormat"
416503
==> "AssemblyInfo"
417504
==> "Build"
505+
==> "Pack"
418506
==> "DeployTestDB"
419507
==> "RunSample"
508+
==> "ValidatePackage"
420509
==> "BuildTestProjects"
421510
==> "RunTests"
422511
==> "All"

nuget/SqlClient.nuspec

Lines changed: 11 additions & 15 deletions
Original file line numberDiff line numberDiff line change
@@ -5,39 +5,35 @@
55
<version>@build.number@</version>
66
<authors>@authors@</authors>
77
<owners>@authors@</owners>
8-
<licenseUrl>http://github.com/fsprojects/FSharp.Data.SqlClient/blob/master/LICENSE.md</licenseUrl>
9-
<projectUrl>http://fsprojects.github.io/FSharp.Data.SqlClient/</projectUrl>
8+
<license type="expression">Apache-2.0</license>
9+
<projectUrl>https://github.com/fsprojects/FSharp.Data.SqlClient</projectUrl>
1010
<iconUrl>https://raw.githubusercontent.com/fsprojects/FSharp.Data.SqlClient/master/docs/files/img/logo.png</iconUrl>
1111
<requireLicenseAcceptance>false</requireLicenseAcceptance>
1212
<description>@description@</description>
1313
<summary>@summary@</summary>
1414
<releaseNotes>@releaseNotes@</releaseNotes>
1515
<copyright>Copyright 2015</copyright>
1616
<tags>@tags@</tags>
17-
<frameworkAssemblies>
18-
<frameworkAssembly assemblyName="System.Data" targetFramework="net462" />
19-
<frameworkAssembly assemblyName="System.Xml" targetFramework="net462" />
20-
</frameworkAssemblies>
2117
<references>
2218
<reference file="FSharp.Data.SqlClient.dll" />
2319
</references>
2420
<dependencies>
25-
<group targetFramework="net462">
26-
<dependency id="FSharp.Core" version="8.0.301" />
27-
</group>
2821
<group targetFramework="netstandard2.0">
2922
<dependency id="FSharp.Core" version="8.0.301" />
30-
<dependency id="System.Data.SqlClient" version="4.9.0" />
31-
<dependency id="System.Configuration.ConfigurationManager" version="9.0.4" />
23+
<dependency id="Microsoft.Data.SqlClient" version="6.1.4" />
24+
<dependency id="System.Configuration.ConfigurationManager" version="9.0.11" />
3225
</group>
33-
<group targetFramework="net8.0">
26+
<group targetFramework="net9.0">
3427
<dependency id="FSharp.Core" version="8.0.301" />
35-
<dependency id="System.Data.SqlClient" version="4.9.0" />
36-
<dependency id="System.Configuration.ConfigurationManager" version="9.0.4" />
28+
<dependency id="Microsoft.Data.SqlClient" version="6.1.4" />
29+
<dependency id="System.Configuration.ConfigurationManager" version="9.0.11" />
3730
</group>
3831
</dependencies>
3932
</metadata>
4033
<files>
41-
<file src="..\bin\**\*.*" exclude="**\*.deps.json" target="lib" />
34+
<file src="..\bin\net9.0\FSharp.Data.SqlClient.*" target="lib\net9.0" />
35+
<file src="..\bin\netstandard2.0\FSharp.Data.SqlClient.*" target="lib\netstandard2.0" />
36+
<file src="..\bin\typeproviders\fsharp41\net9.0\**\*.*" exclude="**\*.deps.json" target="typeproviders\fsharp41\net9.0" />
37+
<file src="..\bin\typeproviders\fsharp41\netstandard2.0\**\*.*" exclude="**\*.deps.json" target="typeproviders\fsharp41\netstandard2.0" />
4238
</files>
4339
</package>

src/SqlClient/SqlClient.fsproj

Lines changed: 45 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -11,6 +11,51 @@
1111
<WithLegacyNamespaces>true</WithLegacyNamespaces>
1212
<DefineConstants Condition="'$(WithLegacyNamespaces)'=='true'">$(DefineConstants);WITH_LEGACY_NAMESPACE</DefineConstants>
1313
</PropertyGroup>
14+
<!-- NuGet package metadata -->
15+
<PropertyGroup>
16+
<PackageId>FSharp.Data.SqlClient</PackageId>
17+
<Authors>Dmitry Morozov;Dmitry Sevastianov</Authors>
18+
<Description>SqlCommandProvider provides statically typed access to input parameters and result set of T-SQL command in idiomatic F# way. SqlProgrammabilityProvider exposes Stored Procedures, User-Defined Types and User-Defined Functions in F# code.</Description>
19+
<PackageLicenseExpression>Apache-2.0</PackageLicenseExpression>
20+
<PackageProjectUrl>https://github.com/fsprojects/FSharp.Data.SqlClient</PackageProjectUrl>
21+
<RepositoryUrl>https://github.com/fsprojects/FSharp.Data.SqlClient</RepositoryUrl>
22+
<RepositoryType>git</RepositoryType>
23+
<PackageTags>F# fsharp data typeprovider sql</PackageTags>
24+
<PackageReadmeFile>README.md</PackageReadmeFile>
25+
<!-- NU5100: assemblies outside lib/ — expected for type-provider design-time content -->
26+
<NoWarn>$(NoWarn);NU5100</NoWarn>
27+
<!-- Register our custom target to run during pack -->
28+
<TargetsForTfmSpecificContentInPackage>$(TargetsForTfmSpecificContentInPackage);_PackDesignTimeProvider</TargetsForTfmSpecificContentInPackage>
29+
</PropertyGroup>
30+
<!--
31+
Design-time provider packaging.
32+
33+
The DesignTime project builds separately (it's in SqlClient.sln) and outputs
34+
to bin/typeproviders/fsharp41/{tfm}/. During pack, we glob those outputs
35+
into the standard typeproviders/fsharp41/{tfm}/ location in the nupkg —
36+
the same layout that the F# SDK's IsFSharpDesignTimeProvider mechanism would
37+
produce.
38+
39+
We glob directly rather than using a ProjectReference with
40+
IsFSharpDesignTimeProvider because the DesignTime project uses a different
41+
(Paket-managed) FSharp.Core version, and a ProjectReference would poison
42+
the runtime project's compilation.
43+
-->
44+
<ItemGroup>
45+
<None Include="..\..\README.md" Pack="true" PackagePath="" />
46+
</ItemGroup>
47+
<Target Name="_PackDesignTimeProvider" BeforeTargets="GenerateNuspec">
48+
<PropertyGroup>
49+
<_DtBaseDir>$([System.IO.Path]::GetFullPath('$(MSBuildThisFileDirectory)..\..\bin\typeproviders\fsharp41'))</_DtBaseDir>
50+
</PropertyGroup>
51+
<ItemGroup>
52+
<_DtOutputFiles Include="$(_DtBaseDir)\$(TargetFramework)\**\*"
53+
Exclude="$(_DtBaseDir)\$(TargetFramework)\**\*.deps.json;$(_DtBaseDir)\$(TargetFramework)\**\FSharp.Core.dll" />
54+
<TfmSpecificPackageFile Include="@(_DtOutputFiles)">
55+
<PackagePath>typeproviders/fsharp41/$(TargetFramework)/%(RecursiveDir)%(Filename)%(Extension)</PackagePath>
56+
</TfmSpecificPackageFile>
57+
</ItemGroup>
58+
</Target>
1459
<PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Debug|AnyCPU'">
1560
<TreatWarningsAsErrors>true</TreatWarningsAsErrors>
1661
<DefineConstants>$(DefineConstants);DEBUG;TRACE</DefineConstants>
Lines changed: 29 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,29 @@
1+
<?xml version="1.0" encoding="utf-8"?>
2+
<!--
3+
Package-validation project.
4+
This project references FSharp.Data.SqlClient ONLY via NuGet PackageReference
5+
(not a ProjectReference or HintPath), so building it proves the .nupkg layout is
6+
correct for all three type providers: SqlCommandProvider, SqlProgrammabilityProvider,
7+
and SqlEnumProvider.
8+
9+
It is NOT part of any .sln and is only built by the "ValidatePackage" build target.
10+
The package version is passed via -p:FSharpDataSqlClientVersion=...
11+
-->
12+
<Project Sdk="Microsoft.NET.Sdk">
13+
<PropertyGroup>
14+
<OutputType>Exe</OutputType>
15+
<TargetFramework>net9.0</TargetFramework>
16+
<AssemblyName>PackageValidation</AssemblyName>
17+
<EnableDefaultCompileItems>false</EnableDefaultCompileItems>
18+
</PropertyGroup>
19+
<ItemGroup>
20+
<Compile Include="Program.fs" />
21+
</ItemGroup>
22+
<ItemGroup>
23+
<None Include="app.config" CopyToOutputDirectory="PreserveNewest" />
24+
</ItemGroup>
25+
<ItemGroup>
26+
<PackageReference Include="FSharp.Data.SqlClient" Version="$(FSharpDataSqlClientVersion)" />
27+
<PackageReference Include="System.Configuration.ConfigurationManager" Version="9.0.11" />
28+
</ItemGroup>
29+
</Project>

tests/PackageValidation/Program.fs

Lines changed: 62 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,62 @@
1+
module PackageValidation
2+
3+
open System
4+
open System.Configuration
5+
open FSharp.Data
6+
7+
[<Literal>]
8+
let ConnName = "name=AdventureWorks"
9+
10+
// ── SqlCommandProvider ──────────────────────────────────────────────────────
11+
type QueryProducts =
12+
SqlCommandProvider<
13+
"SELECT TOP (@top) Name AS ProductName, SellStartDate
14+
FROM Production.Product
15+
WHERE SellStartDate > @SellStartDate",
16+
ConnName
17+
>
18+
19+
// ── SqlProgrammabilityProvider ──────────────────────────────────────────────
20+
type AW = SqlProgrammabilityProvider<ConnName>
21+
22+
// ── SqlEnumProvider ─────────────────────────────────────────────────────────
23+
type SalesReasons = SqlEnumProvider<"SELECT Name, SalesReasonID FROM Sales.SalesReason", ConnName>
24+
25+
[<EntryPoint>]
26+
let main _argv =
27+
let connStr =
28+
ConfigurationManager.ConnectionStrings.["AdventureWorks"].ConnectionString
29+
30+
printfn "=== Package Validation ==="
31+
printfn "Validating FSharp.Data.SqlClient NuGet package via PackageReference"
32+
printfn ""
33+
34+
printfn "--- SqlCommandProvider ---"
35+
36+
use cmd = new QueryProducts(connStr)
37+
let rows = cmd.Execute(top = 3L, SellStartDate = DateTime.Parse("2002-06-01"))
38+
39+
for row in rows do
40+
printfn " %-40s %O" row.ProductName row.SellStartDate
41+
42+
printfn ""
43+
printfn "--- SqlEnumProvider ---"
44+
printfn " Sales-reason 'Price' id = %d" SalesReasons.Price
45+
46+
printfn ""
47+
printfn "--- SqlProgrammabilityProvider ---"
48+
49+
use cmd2 = new AW.dbo.ufnGetContactInformation (connStr)
50+
let contacts = cmd2.Execute(PersonID = 1)
51+
52+
for row in contacts do
53+
printfn
54+
" PersonID=%d Name=%s JobTitle=%s Type=%s"
55+
row.PersonID
56+
row.FirstName.Value
57+
row.JobTitle.Value
58+
row.BusinessEntityType.Value
59+
60+
printfn ""
61+
printfn "=== Package validation succeeded ==="
62+
0

tests/PackageValidation/app.config

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,7 @@
1+
<?xml version="1.0" encoding="utf-8"?>
2+
<configuration>
3+
<connectionStrings>
4+
<add name="AdventureWorks"
5+
connectionString="Data Source=localhost,1433;Initial Catalog=AdventureWorks2012;User ID=SA;Password=YourStrong@Passw0rd;TrustServerCertificate=true" />
6+
</connectionStrings>
7+
</configuration>
Lines changed: 14 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,14 @@
1+
<?xml version="1.0" encoding="utf-8"?>
2+
<!--
3+
Isolated NuGet configuration for package validation.
4+
<clear /> removes inherited sources so we only see:
5+
1. The locally-built .nupkg (temp/nupkg)
6+
2. nuget.org for transitive dependencies (FSharp.Core, etc.)
7+
-->
8+
<configuration>
9+
<packageSources>
10+
<clear />
11+
<add key="local-validation" value="../../temp/nupkg" />
12+
<add key="nuget.org" value="https://api.nuget.org/v3/index.json" />
13+
</packageSources>
14+
</configuration>

0 commit comments

Comments
 (0)