diff --git a/.github/workflows/build.yml b/.github/workflows/build.yml
index ee50c94..3ffaff9 100644
--- a/.github/workflows/build.yml
+++ b/.github/workflows/build.yml
@@ -2,9 +2,9 @@ name: Build (Windows)
on:
push:
- branches: [ "master", "main" ]
+ branches: [ "master", "main", "sow/**" ]
pull_request:
- branches: [ "master", "main" ]
+ branches: [ "master", "main", "sow/**" ]
workflow_dispatch:
jobs:
@@ -35,6 +35,36 @@ jobs:
- name: Setup MSBuild
uses: microsoft/setup-msbuild@v2
+ - name: Compute SAMVersion
+ id: ver
+ shell: pwsh
+ run: |
+ $ErrorActionPreference = 'Stop'
+ # Prefer head_ref when its a sow branch (release-promotion PRs from sow/* to main),
+ # else use base_ref on PR events / ref_name on push events.
+ $headRef = '${{ github.head_ref }}'
+ $baseRef = '${{ github.base_ref }}'
+ $refName = '${{ github.ref_name }}'
+ if ($headRef -match '^sow/\d{4}-Q\d$') {
+ $ref = $headRef
+ } elseif ('${{ github.event_name }}' -eq 'pull_request') {
+ $ref = $baseRef
+ } else {
+ $ref = $refName
+ }
+ # .NET AssemblyVersion components are UInt16 (max 65535). Cap to 60000 for headroom.
+ $run = ${{ github.run_number }} % 60000
+ if ($ref -match 'sow/(\d{4})-Q(\d)') {
+ $v = "$($Matches[1]).$($Matches[2]).$run.0"
+ $src = "branch '$ref'"
+ } else {
+ $now = (Get-Date).ToUniversalTime()
+ $quarter = [int][Math]::Ceiling($now.Month / 3.0)
+ $v = "$($now.Year).$quarter.$run.0"
+ $src = "date $($now.ToString('yyyy-MM-dd')) (ref '$ref' not sow/yyyy-Qx)"
+ }
+ Write-Host "SAMVersion = $v (from $src)"
+ "samversion=$v" | Out-File -FilePath $env:GITHUB_OUTPUT -Append -Encoding utf8
- name: Clone dependency repos (siblings)
shell: pwsh
@@ -55,10 +85,32 @@ jobs:
$deps = $buildOrder[0..($buildOrder.Count-2)]
foreach ($r in $deps) {
if (Test-Path $r) { continue }
- Write-Host "Cloning https://github.com/$org/$r.git"
- git clone --depth 1 "https://github.com/$org/$r.git" $r
+ $headRef = '${{ github.head_ref }}'
+ $baseRef = '${{ github.base_ref }}'
+ $refName = '${{ github.ref_name }}'
+ $candidates = @()
+ if ($headRef) { $candidates += $headRef }
+ # Current sow branch: base_ref on PR events, ref_name on push events.
+ $sowRef = if ($baseRef -match '^sow/') { $baseRef } elseif ($refName -match '^sow/') { $refName } else { '' }
+ if ($sowRef -and $sowRef -ne $headRef) { $candidates += $sowRef }
+ $candidates += 'sow/2026-Q2'
+ $cloned = $false
+ foreach ($cand in $candidates) {
+ $has = (git ls-remote --heads "https://github.com/$org/$r.git" $cand 2>$null | Out-String).Trim()
+ if ($has) {
+ Write-Host "Cloning $org/$r @ $cand"
+ git clone --depth 1 --branch $cand "https://github.com/$org/$r.git" $r
+ $cloned = $true
+ break
+ }
+ }
+ if (-not $cloned) {
+ Write-Host "Cloning $org/$r @ default branch"
+ git clone --depth 1 "https://github.com/$org/$r.git" $r
+ }
}
+
# Ensure ReferencePath exists even before SAM_Windows builds
New-Item -ItemType Directory -Force -Path "SAM_Windows\build" | Out-Null
@@ -77,6 +129,7 @@ jobs:
'/v:m'
'/p:Configuration=Release'
'/p:UseSharedCompilation=false'
+ '/p:SAMVersion=${{ steps.ver.outputs.samversion }}'
'/p:RunPostBuildEvent=OnOutputUpdated'
"/p:ReferencePath=$windowsRef"
)
diff --git a/Directory.Build.props b/Directory.Build.props
new file mode 100644
index 0000000..509f4c6
--- /dev/null
+++ b/Directory.Build.props
@@ -0,0 +1,57 @@
+
+
+
+
+ 1.0.0.0
+ $(SAMVersion)
+ $(SAMVersion)
+
+ $(SAMVersion)+$(SAMSourceRevision)
+
+ false
+ true
+
+
+
+
+
+ <_SAMVersionLine Include="// <auto-generated />" />
+ <_SAMVersionLine Include="[assembly: System.Reflection.AssemblyVersionAttribute("$(SAMVersion)")]" />
+ <_SAMVersionLine Include="[assembly: System.Reflection.AssemblyFileVersionAttribute("$(SAMVersion)")]" />
+
+ <_SAMVersionLine Include="[assembly: System.Reflection.AssemblyInformationalVersionAttribute("$(InformationalVersion)")]" Condition="'$(InformationalVersion)' != ''" />
+
+
+
+
+
+
+
+
+
+
diff --git a/Grasshopper/SAM.Analytical.Grasshopper.Multitasker/Kernel/AssemblyInfo.cs b/Grasshopper/SAM.Analytical.Grasshopper.Multitasker/Kernel/AssemblyInfo.cs
index b373f1e..b6a6b52 100644
--- a/Grasshopper/SAM.Analytical.Grasshopper.Multitasker/Kernel/AssemblyInfo.cs
+++ b/Grasshopper/SAM.Analytical.Grasshopper.Multitasker/Kernel/AssemblyInfo.cs
@@ -1,4 +1,7 @@
-using Grasshopper.Kernel;
+// SPDX-License-Identifier: LGPL-3.0-or-later
+// Copyright (c) 2020–2026 Michal Dengusiak & Jakub Ziolkowski and contributors
+
+using Grasshopper.Kernel;
using System;
using System.Drawing;
using System.IO;
diff --git a/Grasshopper/SAM.Analytical.Grasshopper.Multitasker/Properties/AssemblyInfo.cs b/Grasshopper/SAM.Analytical.Grasshopper.Multitasker/Properties/AssemblyInfo.cs
index 7b18ef1..b2fc33a 100644
--- a/Grasshopper/SAM.Analytical.Grasshopper.Multitasker/Properties/AssemblyInfo.cs
+++ b/Grasshopper/SAM.Analytical.Grasshopper.Multitasker/Properties/AssemblyInfo.cs
@@ -1,4 +1,6 @@
-/*
+// SPDX-License-Identifier: LGPL-3.0-or-later
+// Copyright (c) 2020-2026 Michal Dengusiak & Jakub Ziolkowski and contributors
+/*
* This file is part of the Sustaiable Analytical Model (SAM)
* Copyright (c) 2020, the respective contributors. All rights reserved.
*
@@ -52,6 +54,3 @@
//
// You can specify all the values or you can default the Build and Revision Numbers
// by using the '*' as shown below:
-// [assembly: AssemblyVersion("1.0.*")]
-[assembly: AssemblyVersion("1.0.*")]
-[assembly: AssemblyFileVersion("1.0.*")]
diff --git a/Grasshopper/SAM.Analytical.Grasshopper.Multitasker/SAM.Analytical.Grasshopper.Multitasker.csproj b/Grasshopper/SAM.Analytical.Grasshopper.Multitasker/SAM.Analytical.Grasshopper.Multitasker.csproj
index 6fbd594..b6450ed 100644
--- a/Grasshopper/SAM.Analytical.Grasshopper.Multitasker/SAM.Analytical.Grasshopper.Multitasker.csproj
+++ b/Grasshopper/SAM.Analytical.Grasshopper.Multitasker/SAM.Analytical.Grasshopper.Multitasker.csproj
@@ -10,7 +10,6 @@
false
true
true
- false
false
false
diff --git a/Grasshopper/SAM.Core.Grasshopper.Multitasker/Kernel/AssemblyInfo.cs b/Grasshopper/SAM.Core.Grasshopper.Multitasker/Kernel/AssemblyInfo.cs
index 8187edb..81a9f9f 100644
--- a/Grasshopper/SAM.Core.Grasshopper.Multitasker/Kernel/AssemblyInfo.cs
+++ b/Grasshopper/SAM.Core.Grasshopper.Multitasker/Kernel/AssemblyInfo.cs
@@ -1,4 +1,7 @@
-using Grasshopper.Kernel;
+// SPDX-License-Identifier: LGPL-3.0-or-later
+// Copyright (c) 2020–2026 Michal Dengusiak & Jakub Ziolkowski and contributors
+
+using Grasshopper.Kernel;
using System;
using System.Drawing;
using System.IO;
diff --git a/Grasshopper/SAM.Core.Grasshopper.Multitasker/Properties/AssemblyInfo.cs b/Grasshopper/SAM.Core.Grasshopper.Multitasker/Properties/AssemblyInfo.cs
index 28816a7..3757f7d 100644
--- a/Grasshopper/SAM.Core.Grasshopper.Multitasker/Properties/AssemblyInfo.cs
+++ b/Grasshopper/SAM.Core.Grasshopper.Multitasker/Properties/AssemblyInfo.cs
@@ -1,4 +1,6 @@
-/*
+// SPDX-License-Identifier: LGPL-3.0-or-later
+// Copyright (c) 2020-2026 Michal Dengusiak & Jakub Ziolkowski and contributors
+/*
* This file is part of the Sustaiable Analytical Model (SAM)
* Copyright (c) 2020, the respective contributors. All rights reserved.
*
@@ -52,6 +54,3 @@
//
// You can specify all the values or you can default the Build and Revision Numbers
// by using the '*' as shown below:
-// [assembly: AssemblyVersion("1.0.*")]
-[assembly: AssemblyVersion("1.0.*")]
-[assembly: AssemblyFileVersion("1.0.*")]
diff --git a/Grasshopper/SAM.Core.Grasshopper.Multitasker/SAM.Core.Grasshopper.Multitasker.csproj b/Grasshopper/SAM.Core.Grasshopper.Multitasker/SAM.Core.Grasshopper.Multitasker.csproj
index 45aa3aa..53139d6 100644
--- a/Grasshopper/SAM.Core.Grasshopper.Multitasker/SAM.Core.Grasshopper.Multitasker.csproj
+++ b/Grasshopper/SAM.Core.Grasshopper.Multitasker/SAM.Core.Grasshopper.Multitasker.csproj
@@ -10,7 +10,6 @@
false
true
true
- false
false
false
diff --git a/SAM_Multitasker/SAM.Analytical.Multitasker/SAM.Analytical.Multitasker.csproj b/SAM_Multitasker/SAM.Analytical.Multitasker/SAM.Analytical.Multitasker.csproj
index 30cba0d..c98a2ad 100644
--- a/SAM_Multitasker/SAM.Analytical.Multitasker/SAM.Analytical.Multitasker.csproj
+++ b/SAM_Multitasker/SAM.Analytical.Multitasker/SAM.Analytical.Multitasker.csproj
@@ -12,7 +12,7 @@
-
+
diff --git a/SAM_Multitasker/SAM.Core.Multitasker/Classes/MultitaskerInput.cs b/SAM_Multitasker/SAM.Core.Multitasker/Classes/MultitaskerInput.cs
index f763f8f..c47a01c 100644
--- a/SAM_Multitasker/SAM.Core.Multitasker/Classes/MultitaskerInput.cs
+++ b/SAM_Multitasker/SAM.Core.Multitasker/Classes/MultitaskerInput.cs
@@ -1,4 +1,6 @@
-using Newtonsoft.Json.Linq;
+// SPDX-License-Identifier: LGPL-3.0-or-later
+// Copyright (c) 2020–2026 Michal Dengusiak & Jakub Ziolkowski and contributors
+using System.Text.Json.Nodes;
using System.Collections.Generic;
namespace SAM.Core.Multitasker
@@ -63,9 +65,9 @@ public MultitaskerInput(string name, ValueType valueType, object value)
}
}
- public MultitaskerInput(JObject jObject)
+ public MultitaskerInput(JsonObject jObject)
{
- FromJObject(jObject);
+ FromJsonObject(jObject);
}
public bool Add(MultitaskerVariable multitaskerVariable)
@@ -84,7 +86,7 @@ public bool Add(MultitaskerVariable multitaskerVariable)
return true;
}
- public bool FromJObject(JObject jObject)
+ public bool FromJsonObject(JsonObject jObject)
{
if (jObject == null)
{
@@ -95,8 +97,8 @@ public bool FromJObject(JObject jObject)
{
dictionary = new Dictionary();
- JArray jArray = jObject.Value("MultitaskerVariables");
- foreach(JObject jObject_MultitaskerVariable in jArray)
+ JsonArray jArray = jObject["MultitaskerVariables"] as JsonArray;
+ foreach(JsonObject jObject_MultitaskerVariable in jArray)
{
Add(new MultitaskerVariable(jObject_MultitaskerVariable));
}
@@ -105,14 +107,14 @@ public bool FromJObject(JObject jObject)
return true;
}
- public JObject ToJObject()
+ public JsonObject ToJsonObject()
{
- JObject result = new JObject();
+ JsonObject result = new JsonObject();
result.Add("_type", Core.Query.FullTypeName(this));
if(dictionary != null)
{
- JArray jArray = new JArray();
+ JsonArray jArray = new JsonArray();
foreach(MultitaskerVariable multitaskerVariable in dictionary.Values)
{
@@ -121,7 +123,7 @@ public JObject ToJObject()
continue;
}
- jArray.Add(multitaskerVariable.ToJObject());
+ jArray.Add(multitaskerVariable.ToJsonObject());
}
result.Add("MultitaskerVariables", jArray);
diff --git a/SAM_Multitasker/SAM.Core.Multitasker/Classes/MultitaskerOutput.cs b/SAM_Multitasker/SAM.Core.Multitasker/Classes/MultitaskerOutput.cs
index a35b384..ad324f7 100644
--- a/SAM_Multitasker/SAM.Core.Multitasker/Classes/MultitaskerOutput.cs
+++ b/SAM_Multitasker/SAM.Core.Multitasker/Classes/MultitaskerOutput.cs
@@ -1,4 +1,6 @@
-using Newtonsoft.Json.Linq;
+// SPDX-License-Identifier: LGPL-3.0-or-later
+// Copyright (c) 2020–2026 Michal Dengusiak & Jakub Ziolkowski and contributors
+using System.Text.Json.Nodes;
using System;
using System.Drawing;
@@ -24,9 +26,9 @@ public MultitaskerOutput(MultitaskerOutput multitaskerOutput)
}
}
- public MultitaskerOutput(JObject jObject)
+ public MultitaskerOutput(JsonObject jObject)
{
- FromJObject(jObject);
+ FromJsonObject(jObject);
}
public object Result
@@ -37,11 +39,11 @@ public object Result
}
}
- public bool FromJObject(JObject jObject)
+ public bool FromJsonObject(JsonObject jObject)
{
if(jObject.ContainsKey("ValueType"))
{
- valueType = Core.Query.Enum(jObject.Value("ValueType"));
+ valueType = Core.Query.Enum(jObject["ValueType"]?.GetValue() ?? default(string));
}
if(jObject.ContainsKey("Result"))
@@ -49,23 +51,23 @@ public bool FromJObject(JObject jObject)
switch (valueType)
{
case ValueType.Boolean:
- result = jObject.Value("Result");
+ result = jObject["Result"]?.GetValue() ?? default(bool);
return true;
case ValueType.Color:
- result = new SAMColor(jObject.Value("Result")).ToColor();
+ result = new SAMColor(jObject["Result"] as JsonObject).ToColor();
return true;
case ValueType.DateTime:
- result = jObject.Value("Result");
+ result = jObject["Result"]?.GetValue() ?? default(DateTime);
return true;
case ValueType.Double:
- result = jObject.Value("Result");
+ result = jObject["Result"]?.GetValue() ?? default(double);
return true;
case ValueType.Guid:
- if (!Enum.TryParse(jObject.Value("Result"), out Guid guid))
+ if (!Enum.TryParse(jObject["Result"]?.GetValue() ?? default(string), out Guid guid))
{
return false;
}
@@ -73,7 +75,7 @@ public bool FromJObject(JObject jObject)
return true;
case ValueType.IJSAMObject:
- JObject jObject_Temp = jObject.Value("Result");
+ JsonObject jObject_Temp = jObject["Result"] as JsonObject;
if (jObject_Temp == null)
{
return false;
@@ -83,11 +85,11 @@ public bool FromJObject(JObject jObject)
return true;
case ValueType.Integer:
- result = jObject.Value("Result");
+ result = jObject["Result"]?.GetValue() ?? default(int);
return true;
case ValueType.String:
- result = jObject.Value("Result");
+ result = jObject["Result"]?.GetValue() ?? default(string);
return true;
}
}
@@ -95,9 +97,9 @@ public bool FromJObject(JObject jObject)
return true;
}
- public JObject ToJObject()
+ public JsonObject ToJsonObject()
{
- JObject jObject = new JObject();
+ JsonObject jObject = new JsonObject();
jObject.Add("_type", Core.Query.FullTypeName(this));
jObject.Add("ValueType", valueType.ToString());
@@ -117,7 +119,7 @@ public JObject ToJObject()
case ValueType.Color:
if (Core.Query.TryConvert(result, out Color color))
{
- value = new SAMColor(color).ToJObject();
+ value = new SAMColor(color).ToJsonObject();
}
break;
@@ -143,7 +145,7 @@ public JObject ToJObject()
break;
case ValueType.IJSAMObject:
- value = ((IJSAMObject)result).ToJObject();
+ value = ((IJSAMObject)result).ToJsonObject();
break;
case ValueType.Integer:
diff --git a/SAM_Multitasker/SAM.Core.Multitasker/Classes/MultitaskerVariable.cs b/SAM_Multitasker/SAM.Core.Multitasker/Classes/MultitaskerVariable.cs
index 62ddd5f..db6607e 100644
--- a/SAM_Multitasker/SAM.Core.Multitasker/Classes/MultitaskerVariable.cs
+++ b/SAM_Multitasker/SAM.Core.Multitasker/Classes/MultitaskerVariable.cs
@@ -1,6 +1,9 @@
-using Newtonsoft.Json.Linq;
-using System.Drawing;
+// SPDX-License-Identifier: LGPL-3.0-or-later
+// Copyright (c) 2020–2026 Michal Dengusiak & Jakub Ziolkowski and contributors
using System;
+using System.Drawing;
+using System.Text.Json;
+using System.Text.Json.Nodes;
namespace SAM.Core.Multitasker
{
@@ -44,9 +47,9 @@ public MultitaskerVariable(MultitaskerVariable multitaskerVariable)
}
}
- public MultitaskerVariable(JObject jObject)
+ public MultitaskerVariable(JsonObject jObject)
{
- FromJObject(jObject);
+ FromJsonObject(jObject);
}
public string Name
@@ -83,7 +86,7 @@ public T GetObject()
return default;
}
- public bool FromJObject(JObject jObject)
+ public bool FromJsonObject(JsonObject jObject)
{
if (jObject == null)
{
@@ -92,17 +95,17 @@ public bool FromJObject(JObject jObject)
if (jObject.ContainsKey("Name"))
{
- name = jObject.Value("Name");
+ name = jObject["Name"]?.GetValue() ?? default(string);
}
if (jObject.ContainsKey("ValueType"))
{
- valueType = Core.Query.Enum(jObject.Value("Name"));
+ valueType = Core.Query.Enum(jObject["Name"]?.GetValue() ?? default(string));
}
if(jObject.ContainsKey("Value"))
{
- object value = jObject.Value