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
6 changes: 6 additions & 0 deletions .github/workflows/build.yml
Original file line number Diff line number Diff line change
Expand Up @@ -41,6 +41,12 @@ jobs:
GENPRES_DEBUG: 1
run: dotnet run ServerTests

- name: Check solution version provenance
# Calls the script directly rather than `dotnet run CheckVersions` so the FAKE target that depends
# on Build, which transitively depends on Clean/RestoreClient, so it would redo the full restore/build
# which the "Test execution" just did. The DLLs are already fresh from build at this point.
run: dotnet fsi scripts/CheckSolutionVersions.fsx

- name: Publish test results
if: always() && runner.os == 'Linux'
uses: EnricoMi/publish-unit-test-result-action@d0a4676d0e0b938bc201470d88276b7c74c712b3 # v2.24.0
Expand Down
3 changes: 3 additions & 0 deletions Build.fs
Original file line number Diff line number Diff line change
Expand Up @@ -211,6 +211,8 @@ Target.create
"No tests were discovered or run. The solution was likely not built/restored before 'dotnet test'."
)

Target.create "CheckVersions" (fun _ -> run dotnet [ "fsi"; "scripts/CheckSolutionVersions.fsx" ] ".")


Target.create
"TestHeadless"
Expand Down Expand Up @@ -290,6 +292,7 @@ let dependencies =
"RestoreClient" ==> "Build" ==> "WatchTests"

"Build" ==> "ServerTests"
"Build" ==> "CheckVersions"
]


Expand Down
1 change: 1 addition & 0 deletions DEVELOPMENT.md
Original file line number Diff line number Diff line change
Expand Up @@ -62,6 +62,7 @@ For example, `dotnet run` (no target) runs the `Run` target, which depends on:
| `dotnet run Clean` | `Clean` | Remove `deploy/` and `dist/` artefacts, delete Fable-generated `.jsx` files |
| `dotnet run Bundle` | `Bundle` | Production build: publish server, compile client, copy data |
| `dotnet run ServerTests` | `ServerTests` | Run all F# unit tests (Expecto) with quiet logging |
| `dotnet run CheckVersions` | `CheckVersions` | Verify every built DLL's version matches the root `Directory.Build.props` |
| `dotnet run TestHeadless` | `TestHeadless` | Build and run tests without launching a browser |
| `dotnet run WatchTests` | `WatchTests` | Run tests in watch mode (re-runs on file changes) |
| `dotnet run Format` | `Format` | Format all F# source files using Fantomas |
Expand Down
5 changes: 5 additions & 0 deletions Directory.Build.props
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
<Project>
<PropertyGroup>
<Version>0.1.2-alpha</Version>
</PropertyGroup>
</Project>
140 changes: 140 additions & 0 deletions scripts/CheckSolutionVersions.fsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,140 @@
// Check Solution Versions
//
// Proves that every project shipped in GenPRES.sln reports the same
// version as the repo-root Directory.Build.props, by inspecting each
// built DLL's actual file-version metadata — not just re-reading the
// XML that's supposed to produce it.
//
// Prerequisite: run `dotnet build GenPRES.sln` first so the DLLs exist.
// Run with: dotnet fsi scripts/CheckSolutionVersions.fsx

open System
open System.IO
open System.Diagnostics
open System.Xml.Linq

// ---------------------------------------------------------------------------
// Configuration
// ---------------------------------------------------------------------------

let repoRoot = Path.GetFullPath(Path.Combine(__SOURCE_DIRECTORY__, ".."))
let slnPath = Path.Combine(repoRoot, "GenPRES.sln")
let propsPath = Path.Combine(repoRoot, "Directory.Build.props")
let configuration = "Debug"
let targetFramework = "net10.0"

// ---------------------------------------------------------------------------
// Expected version, read from the single source of truth
// ---------------------------------------------------------------------------

let expectedVersion =
let doc = XDocument.Load(propsPath)
let ns = doc.Root.Name.Namespace

doc.Descendants(ns + "Version")
|> Seq.tryHead
|> Option.map _.Value
|> Option.defaultWith (fun () -> failwith $"No <Version> found in %s{propsPath}")

// ---------------------------------------------------------------------------
// Projects actually shipped, per GenPRES.sln
// ---------------------------------------------------------------------------

let projectsInSln =
let psi =
ProcessStartInfo(
"dotnet",
$"sln \"%s{slnPath}\" list",
RedirectStandardOutput = true,
UseShellExecute = false
)

use proc = Process.Start psi
let output = proc.StandardOutput.ReadToEnd()
proc.WaitForExit()

if proc.ExitCode <> 0 then
failwith $"`dotnet sln %s{slnPath} list` failed with exit code %i{proc.ExitCode}"

output.Split('\n')
|> Array.map (fun l -> l.Trim().Replace('\\', '/'))
|> Array.filter (fun l -> l.EndsWith ".fsproj")
|> Array.distinct
|> Array.sort
|> List.ofArray

// ---------------------------------------------------------------------------
// Per-project Version check
// ---------------------------------------------------------------------------

[<RequireQualifiedAccess>]
type VersionResult =
| InSync of fileVersion: string * productVersion: string
| Mismatch of fileVersion: string * productVersion: string
| NotBuilt of expectedDll: string


let checkProject (relativeFsproj: string) =
let fullFsproj = Path.Combine(repoRoot, relativeFsproj.Replace('/', Path.DirectorySeparatorChar))
let dir = Path.GetDirectoryName(fullFsproj)
let name = Path.GetFileNameWithoutExtension(fullFsproj)
let dll = Path.Combine(dir, "bin", configuration, targetFramework, $"{name}.dll")

if not (File.Exists dll) then
VersionResult.NotBuilt dll
else
let vi = FileVersionInfo.GetVersionInfo(dll)

let orEmpty =
function
| null -> ""
| (v: string) -> v

let fileVersion = vi.FileVersion |> orEmpty
let productVersion = vi.ProductVersion |> orEmpty
let productBase = productVersion.Split('+') |> Array.head

if productBase = expectedVersion then
VersionResult.InSync(fileVersion, productVersion)
else
VersionResult.Mismatch(fileVersion, productVersion)


// ---------------------------------------------------------------------------
// Run
// ---------------------------------------------------------------------------

printfn $"Expected version (from %s{propsPath}): %s{expectedVersion}"
printfn $"Projects declared in GenPRES.sln: %i{projectsInSln.Length}{Environment.NewLine}"

let results = projectsInSln |> List.map (fun p -> p, checkProject p)

for proj, result in results do
let projName = Path.GetFileName proj

match result with
| VersionResult.InSync(fv, pv) ->
printfn $"MATCH: %-45s{projName} FileVersion=%-10s{fv} ProductVersion=%s{pv}"
| VersionResult.Mismatch(fv, pv) ->
printfn $"MISMATCH: %-45s{projName} FileVersion=%-10s{fv} ProductVersion=%s{pv} (expected %s{expectedVersion})"
| VersionResult.NotBuilt dll ->
printfn $"NOT BUILT: %-45s{projName} (expected at %s{dll})"

let inSync, mismatched, notBuilt =
results
|> List.fold
(fun (inSync, mismatched, notBuilt) (_, r) ->
match r with
| VersionResult.InSync _ -> inSync + 1, mismatched, notBuilt
| VersionResult.Mismatch _ -> inSync, mismatched + 1, notBuilt
| VersionResult.NotBuilt _ -> inSync, mismatched, notBuilt + 1
)
(0, 0, 0)

printfn $"{Environment.NewLine}Summary: %i{inSync} in sync, %i{mismatched} mismatched, %i{notBuilt} not built (of %i{results.Length})"

if mismatched > 0 || notBuilt > 0 then
printfn $"{Environment.NewLine}Version check FAILED."
exit 1
else
printfn $"{Environment.NewLine}Version check PASSED - every shipped project's DLL matches Directory.Build.props."
5 changes: 4 additions & 1 deletion src/Informedica.GenFORM.Lib/Directory.Build.props
Original file line number Diff line number Diff line change
@@ -1,4 +1,8 @@
<Project>
<!-- Pulls <Version> from the repo-root Directory.Build.props so every -->
<!-- library and the app share a single version number. -->
<Import Project="$([MSBuild]::GetPathOfFileAbove('Directory.Build.props', '$(MSBuildThisFileDirectory)../'))" />

<PropertyGroup>
<!-- summary is not migrated from project.json, but you can use the <Description> property for that if needed. -->
<PackageTags>f#, fsharp, medical</PackageTags>
Expand All @@ -8,7 +12,6 @@
<PackageRequireLicenseAcceptance>false</PackageRequireLicenseAcceptance>
<RepositoryType>git</RepositoryType>
<PackageId>Informedica.GenForm.Lib</PackageId>
<Version>1.2.4</Version>
<Authors>halcwb</Authors>
<Company>Informedica</Company>
<RepositoryUrl>https://github.com/informedica/Informedica.GenForm.Lib</RepositoryUrl>
Expand Down
5 changes: 4 additions & 1 deletion src/Informedica.GenORDER.Lib/Directory.Build.props
Original file line number Diff line number Diff line change
@@ -1,4 +1,8 @@
<Project>
<!-- Pulls <Version> from the repo-root Directory.Build.props so every -->
<!-- library and the app share a single version number. -->
<Import Project="$([MSBuild]::GetPathOfFileAbove('Directory.Build.props', '$(MSBuildThisFileDirectory)../'))" />

<PropertyGroup>
<!-- summary is not migrated from project.json, but you can use the <Description> property for that if needed. -->
<PackageTags>f#, fsharp, medical</PackageTags>
Expand All @@ -7,7 +11,6 @@
<PackageRequireLicenseAcceptance>false</PackageRequireLicenseAcceptance>
<RepositoryType>git</RepositoryType>
<PackageId>Informedica.GenOrder.Lib</PackageId>
<Version>1.0.5</Version>
<Authors>halcwb</Authors>
<Company>Informedica</Company>
<RepositoryUrl>https://github.com/informedica/Informedica.GenOrder.Lib</RepositoryUrl>
Expand Down
5 changes: 4 additions & 1 deletion src/Informedica.GenSOLVER.Lib/Directory.Build.props
Original file line number Diff line number Diff line change
@@ -1,4 +1,8 @@
<Project>
<!-- Pulls <Version> from the repo-root Directory.Build.props so every -->
<!-- library and the app share a single version number. -->
<Import Project="$([MSBuild]::GetPathOfFileAbove('Directory.Build.props', '$(MSBuildThisFileDirectory)../'))" />

<PropertyGroup>
<!-- summary is not migrated from project.json, but you can use the <Description> property for that if needed. -->
<PackageTags>f#, fsharp, medical</PackageTags>
Expand All @@ -7,7 +11,6 @@
<PackageRequireLicenseAcceptance>false</PackageRequireLicenseAcceptance>
<RepositoryType>git</RepositoryType>
<PackageId>Informedica.GenSolver.Lib</PackageId>
<Version>1.0.0</Version>
<Authors>halcwb</Authors>
<Company>Informedica</Company>
<RepositoryUrl>https://github.com/informedica/Informedica.GenSolver.Lib</RepositoryUrl>
Expand Down
39 changes: 0 additions & 39 deletions src/Informedica.GenUNITS.Lib/AssemblyInfo.fs

This file was deleted.

5 changes: 4 additions & 1 deletion src/Informedica.GenUNITS.Lib/Directory.Build.props
Original file line number Diff line number Diff line change
@@ -1,4 +1,8 @@
<Project>
<!-- Pulls <Version> from the repo-root Directory.Build.props so every -->
<!-- library and the app share a single version number. -->
<Import Project="$([MSBuild]::GetPathOfFileAbove('Directory.Build.props', '$(MSBuildThisFileDirectory)../'))" />

<PropertyGroup>
<!-- summary is not migrated from project.json, but you can use the <Description> property for that if needed. -->
<PackageTags>f#, fsharp</PackageTags>
Expand All @@ -7,7 +11,6 @@
<PackageRequireLicenseAcceptance>false</PackageRequireLicenseAcceptance>
<RepositoryType>git</RepositoryType>
<PackageId>Informedica.GenUnits.Lib</PackageId>
<Version>1.0.3</Version>
<Authors>halcwb</Authors>
<Company>Informedica</Company>
<RepositoryUrl>https://github.com/informedica/Informedica.GenUnits.Lib</RepositoryUrl>
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,6 @@
</PropertyGroup>
<ItemGroup>
<None Include="paket.references" />
<Compile Include="AssemblyInfo.fs" />
<Compile Include="Utils.fs" />
<Compile Include="Types.fs" />
<Compile Include="Core.fs" />
Expand Down
5 changes: 4 additions & 1 deletion src/Informedica.NKF.Lib/Directory.Build.props
Original file line number Diff line number Diff line change
@@ -1,4 +1,8 @@
<Project>
<!-- Pulls <Version> from the repo-root Directory.Build.props so every -->
<!-- library and the app share a single version number. -->
<Import Project="$([MSBuild]::GetPathOfFileAbove('Directory.Build.props', '$(MSBuildThisFileDirectory)../'))" />

<PropertyGroup>
<!-- summary is not migrated from project.json, but you can use the <Description> property for that if needed. -->
<PackageTags>f#, fsharp, medical</PackageTags>
Expand All @@ -8,7 +12,6 @@
<PackageRequireLicenseAcceptance>false</PackageRequireLicenseAcceptance>
<RepositoryType>git</RepositoryType>
<PackageId>Informedica.GenForm.Lib</PackageId>
<Version>1.2.4</Version>
<Authors>halcwb</Authors>
<Company>Informedica</Company>
<RepositoryUrl>https://github.com/informedica/Informedica.GenForm.Lib</RepositoryUrl>
Expand Down
39 changes: 0 additions & 39 deletions src/Informedica.Utils.Lib/AssemblyInfo.fs

This file was deleted.

5 changes: 4 additions & 1 deletion src/Informedica.Utils.Lib/Directory.Build.props
Original file line number Diff line number Diff line change
@@ -1,4 +1,8 @@
<Project>
<!-- Pulls <Version> from the repo-root Directory.Build.props so every -->
<!-- library and the app share a single version number. -->
<Import Project="$([MSBuild]::GetPathOfFileAbove('Directory.Build.props', '$(MSBuildThisFileDirectory)../'))" />

<PropertyGroup>
<!-- summary is not migrated from project.json, but you can use the <Description> property for that if needed. -->
<PackageTags>f#, fsharp</PackageTags>
Expand All @@ -8,7 +12,6 @@
<PackageRequireLicenseAcceptance>false</PackageRequireLicenseAcceptance>
<RepositoryType>git</RepositoryType>
<PackageId>Informedica.Utils.Lib</PackageId>
<Version>1.0.5</Version>
<Authors>halcwb</Authors>
<Company>Informedica</Company>
<RepositoryUrl>https://github.com/informedica/Informedica.Utils.Lib</RepositoryUrl>
Expand Down
1 change: 0 additions & 1 deletion src/Informedica.Utils.Lib/Informedica.Utils.Lib.fsproj
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,6 @@
<WarnOn>3390;$(WarnOn)</WarnOn>
</PropertyGroup>
<ItemGroup>
<Compile Include="AssemblyInfo.fs" />
<Compile Include="Memoization.fs" />
<Compile Include="Reflection.fs" />
<Compile Include="NullCheck.fs" />
Expand Down
Loading