From 37460eb1aa010f9b41a89e2b07229d50565ca168 Mon Sep 17 00:00:00 2001 From: Michal Dengusiak Date: Fri, 15 May 2026 17:01:36 +0200 Subject: [PATCH 1/9] Migrate to System.Text.Json (replace Newtonsoft.Json) SAM-BIM core migrated from Newtonsoft.Json to System.Text.Json.Nodes. - Replace each csproj's Newtonsoft package reference with System.Text.Json 10.0.8 - MultitaskerInput, MultitaskerOutput, MultitaskerVariable, Script: FromJObject/ToJObject -> FromJsonObject/ToJsonObject; JObject -> JsonObject; .Value(key) -> indexer + GetValue(); .Value(key) -> indexer + Deserialize() - Resource scripts (files/resources/Core/Multitasker/scripts/*.cs) are Roslyn-compiled at runtime against the consumer's SAM.Core, so they need the same migration: drop unused 'using Newtonsoft.Json' and rewrite TestScript.cs's JsonConvert.DeserializeObject -> JsonNode.Parse Co-Authored-By: Claude Opus 4.7 --- .../SAM.Analytical.Multitasker.csproj | 2 +- .../Classes/MultitaskerInput.cs | 20 +++++------ .../Classes/MultitaskerOutput.cs | 34 +++++++++---------- .../Classes/MultitaskerVariable.cs | 27 ++++++++------- .../SAM.Core.Multitasker/Classes/Script.cs | 18 +++++----- .../SAM.Core.Multitasker.csproj | 2 +- .../Core/Multitasker/scripts/RunWorkflow.cs | 1 - .../scripts/RunWorkflow_NotSave.cs | 1 - .../Multitasker/scripts/RunWorkflow_Save.cs | 1 - .../scripts/RunWorkflow_WeatherData.cs | 1 - .../Core/Multitasker/scripts/TestScript.cs | 3 +- 11 files changed, 53 insertions(+), 57 deletions(-) diff --git a/SAM_Multitasker/SAM.Analytical.Multitasker/SAM.Analytical.Multitasker.csproj b/SAM_Multitasker/SAM.Analytical.Multitasker/SAM.Analytical.Multitasker.csproj index 30cba0d..1ff330a 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..4db1614 100644 --- a/SAM_Multitasker/SAM.Core.Multitasker/Classes/MultitaskerInput.cs +++ b/SAM_Multitasker/SAM.Core.Multitasker/Classes/MultitaskerInput.cs @@ -1,4 +1,4 @@ -using Newtonsoft.Json.Linq; +using System.Text.Json.Nodes; using System.Collections.Generic; namespace SAM.Core.Multitasker @@ -63,9 +63,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 +84,7 @@ public bool Add(MultitaskerVariable multitaskerVariable) return true; } - public bool FromJObject(JObject jObject) + public bool FromJsonObject(JsonObject jObject) { if (jObject == null) { @@ -95,8 +95,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 +105,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 +121,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..c66841b 100644 --- a/SAM_Multitasker/SAM.Core.Multitasker/Classes/MultitaskerOutput.cs +++ b/SAM_Multitasker/SAM.Core.Multitasker/Classes/MultitaskerOutput.cs @@ -1,4 +1,4 @@ -using Newtonsoft.Json.Linq; +using System.Text.Json.Nodes; using System; using System.Drawing; @@ -24,9 +24,9 @@ public MultitaskerOutput(MultitaskerOutput multitaskerOutput) } } - public MultitaskerOutput(JObject jObject) + public MultitaskerOutput(JsonObject jObject) { - FromJObject(jObject); + FromJsonObject(jObject); } public object Result @@ -37,11 +37,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 +49,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 +73,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 +83,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 +95,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 +117,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 +143,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..f70480d 100644 --- a/SAM_Multitasker/SAM.Core.Multitasker/Classes/MultitaskerVariable.cs +++ b/SAM_Multitasker/SAM.Core.Multitasker/Classes/MultitaskerVariable.cs @@ -1,6 +1,7 @@ -using Newtonsoft.Json.Linq; +using System; using System.Drawing; -using System; +using System.Text.Json; +using System.Text.Json.Nodes; namespace SAM.Core.Multitasker { @@ -44,9 +45,9 @@ public MultitaskerVariable(MultitaskerVariable multitaskerVariable) } } - public MultitaskerVariable(JObject jObject) + public MultitaskerVariable(JsonObject jObject) { - FromJObject(jObject); + FromJsonObject(jObject); } public string Name @@ -83,7 +84,7 @@ public T GetObject() return default; } - public bool FromJObject(JObject jObject) + public bool FromJsonObject(JsonObject jObject) { if (jObject == null) { @@ -92,17 +93,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("Value"); + object value = jObject["Value"]?.Deserialize(); switch(valueType) { case ValueType.String: @@ -155,7 +156,7 @@ public bool FromJObject(JObject jObject) break; case ValueType.Undefined: - if(value is JObject) + if(value is JsonObject) { IJSAMObject jSAMObject_Undefined = Core.Query.IJSAMObject(jObject); if (jSAMObject_Undefined != null) @@ -173,9 +174,9 @@ 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)); ValueType valueType = ValueType; @@ -196,7 +197,7 @@ public JObject ToJObject() case ValueType.Color: if (Core.Query.TryConvert(Value, out Color color)) { - value = new SAMColor(color).ToJObject(); + value = new SAMColor(color).ToJsonObject(); } break; @@ -222,7 +223,7 @@ public JObject ToJObject() break; case ValueType.IJSAMObject: - value = ((IJSAMObject)Value).ToJObject(); + value = ((IJSAMObject)Value).ToJsonObject(); break; case ValueType.Integer: diff --git a/SAM_Multitasker/SAM.Core.Multitasker/Classes/Script.cs b/SAM_Multitasker/SAM.Core.Multitasker/Classes/Script.cs index 696fafb..7c92e88 100644 --- a/SAM_Multitasker/SAM.Core.Multitasker/Classes/Script.cs +++ b/SAM_Multitasker/SAM.Core.Multitasker/Classes/Script.cs @@ -1,5 +1,5 @@  -using Newtonsoft.Json.Linq; +using System.Text.Json.Nodes; using System; namespace SAM.Core.Multitasker @@ -20,9 +20,9 @@ public Script(Script script) } } - public Script(JObject jObject) + public Script(JsonObject jObject) { - FromJObject(jObject); + FromJsonObject(jObject); } public Script(ProgrammingLanguage programmingLanguage, string name, string code) @@ -65,7 +65,7 @@ public override bool Equals(object @object) return script.code == code && script.programmingLanguage == programmingLanguage && script.name == name; } - public bool FromJObject(JObject jObject) + public bool FromJsonObject(JsonObject jObject) { if(jObject == null) { @@ -74,17 +74,17 @@ public bool FromJObject(JObject jObject) if(jObject.ContainsKey("Code")) { - code = jObject.Value("Code"); + code = jObject["Code"]?.GetValue() ?? default(string); } if (jObject.ContainsKey("Name")) { - name = jObject.Value("Name"); + name = jObject["Name"]?.GetValue() ?? default(string); } if (jObject.ContainsKey("ProgrammingLanguage")) { - programmingLanguage = Core.Query.Enum(jObject.Value("ProgrammingLanguage")); + programmingLanguage = Core.Query.Enum(jObject["ProgrammingLanguage"]?.GetValue() ?? default(string)); } return true; @@ -149,9 +149,9 @@ public static implicit operator string(Script script) return script.code; } - public JObject ToJObject() + public JsonObject ToJsonObject() { - JObject result = new JObject(); + JsonObject result = new JsonObject(); result.Add("_type", Core.Query.FullTypeName(this)); if(code != null) diff --git a/SAM_Multitasker/SAM.Core.Multitasker/SAM.Core.Multitasker.csproj b/SAM_Multitasker/SAM.Core.Multitasker/SAM.Core.Multitasker.csproj index ff402c0..fbf0dd6 100644 --- a/SAM_Multitasker/SAM.Core.Multitasker/SAM.Core.Multitasker.csproj +++ b/SAM_Multitasker/SAM.Core.Multitasker/SAM.Core.Multitasker.csproj @@ -10,7 +10,7 @@ - + diff --git a/files/resources/Core/Multitasker/scripts/RunWorkflow.cs b/files/resources/Core/Multitasker/scripts/RunWorkflow.cs index deed237..62732de 100644 --- a/files/resources/Core/Multitasker/scripts/RunWorkflow.cs +++ b/files/resources/Core/Multitasker/scripts/RunWorkflow.cs @@ -1,4 +1,3 @@ -using Newtonsoft.Json; using SAM.Core; using SAM.Analytical; using SAM.Analytical.Tas; diff --git a/files/resources/Core/Multitasker/scripts/RunWorkflow_NotSave.cs b/files/resources/Core/Multitasker/scripts/RunWorkflow_NotSave.cs index 9452b51..015886f 100644 --- a/files/resources/Core/Multitasker/scripts/RunWorkflow_NotSave.cs +++ b/files/resources/Core/Multitasker/scripts/RunWorkflow_NotSave.cs @@ -1,4 +1,3 @@ -using Newtonsoft.Json; using SAM.Core; using SAM.Analytical; using SAM.Analytical.Tas; diff --git a/files/resources/Core/Multitasker/scripts/RunWorkflow_Save.cs b/files/resources/Core/Multitasker/scripts/RunWorkflow_Save.cs index 0226366..bac322f 100644 --- a/files/resources/Core/Multitasker/scripts/RunWorkflow_Save.cs +++ b/files/resources/Core/Multitasker/scripts/RunWorkflow_Save.cs @@ -1,4 +1,3 @@ -using Newtonsoft.Json; using SAM.Core; using SAM.Analytical; using SAM.Analytical.Tas; diff --git a/files/resources/Core/Multitasker/scripts/RunWorkflow_WeatherData.cs b/files/resources/Core/Multitasker/scripts/RunWorkflow_WeatherData.cs index ba6e8d7..5c8b6bf 100644 --- a/files/resources/Core/Multitasker/scripts/RunWorkflow_WeatherData.cs +++ b/files/resources/Core/Multitasker/scripts/RunWorkflow_WeatherData.cs @@ -1,4 +1,3 @@ -using Newtonsoft.Json; using SAM.Core; using SAM.Analytical; using SAM.Analytical.Tas; diff --git a/files/resources/Core/Multitasker/scripts/TestScript.cs b/files/resources/Core/Multitasker/scripts/TestScript.cs index da08161..8ceb24d 100644 --- a/files/resources/Core/Multitasker/scripts/TestScript.cs +++ b/files/resources/Core/Multitasker/scripts/TestScript.cs @@ -1,10 +1,9 @@ -using Newtonsoft.Json; using SAM.Core; int index = SAM.Core.Query.IndexOf("abcd", 'c'); var json = "{\"name\":\"John\", \"age\":30}"; -dynamic person = Newtonsoft.Json.JsonConvert.DeserializeObject(json); +dynamic person = System.Text.Json.Nodes.JsonNode.Parse(json); int i = 1; int j = 2; From 969cfc9f5760a5087c405dc7509eb5ce4f586999 Mon Sep 17 00:00:00 2001 From: Michal Dengusiak Date: Fri, 15 May 2026 22:00:49 +0200 Subject: [PATCH 2/9] CI: prefer sow/2026-Q2 for dep clones + trigger on it During the SAM-BIM Newtonsoft.Json -> System.Text.Json migration the quarterly sow/2026-Q2 branch carries the binary-breaking change. CI needs to consume the migrated SAM (and any sibling dep) from sow/2026-Q2, not from master, until the quarter-end merge. - Add "sow/2026-Q2" to push/pull_request branch triggers - For each dep clone, ls-remote sow/2026-Q2 and prefer it when present; fall back to default branch (e.g. master) otherwise. After the quarter merges back to master, this fallback naturally restores prior behaviour. Co-Authored-By: Claude Opus 4.7 --- .github/workflows/build.yml | 15 +++++++++++---- 1 file changed, 11 insertions(+), 4 deletions(-) diff --git a/.github/workflows/build.yml b/.github/workflows/build.yml index ee50c94..c998087 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/2026-Q2" ] pull_request: - branches: [ "master", "main" ] + branches: [ "master", "main", "sow/2026-Q2" ] workflow_dispatch: jobs: @@ -55,8 +55,15 @@ 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 + $preferredBranch = 'sow/2026-Q2' + $hasPreferred = (git ls-remote --heads "https://github.com/$org/$r.git" $preferredBranch 2>$null | Out-String).Trim() + if ($hasPreferred) { + Write-Host "Cloning $org/$r @ $preferredBranch" + git clone --depth 1 --branch $preferredBranch "https://github.com/$org/$r.git" $r + } else { + 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 From 89c6b4f1024d48265792324d3338f73cd9a34cd5 Mon Sep 17 00:00:00 2001 From: Michal Dengusiak Date: Fri, 15 May 2026 22:05:35 +0200 Subject: [PATCH 3/9] CI: cascade dep clones to PR head_ref -> sow/2026-Q2 -> default Refines the previous workflow patch so CI on the migration PR can succeed before anything has been merged. Each dep repo is cloned from: 1. github.head_ref (feature/remove-newtonsoft on these PRs) - has the migration right now on every dep 2. sow/2026-Q2 - source of truth after these PRs merge 3. default branch - source of truth after quarter-end merge Co-Authored-By: Claude Opus 4.7 --- .github/workflows/build.yml | 22 ++++++++++++++++------ 1 file changed, 16 insertions(+), 6 deletions(-) diff --git a/.github/workflows/build.yml b/.github/workflows/build.yml index c998087..725bc93 100644 --- a/.github/workflows/build.yml +++ b/.github/workflows/build.yml @@ -55,17 +55,27 @@ jobs: $deps = $buildOrder[0..($buildOrder.Count-2)] foreach ($r in $deps) { if (Test-Path $r) { continue } - $preferredBranch = 'sow/2026-Q2' - $hasPreferred = (git ls-remote --heads "https://github.com/$org/$r.git" $preferredBranch 2>$null | Out-String).Trim() - if ($hasPreferred) { - Write-Host "Cloning $org/$r @ $preferredBranch" - git clone --depth 1 --branch $preferredBranch "https://github.com/$org/$r.git" $r - } else { + $headRef = '${{ github.head_ref }}' + $candidates = @() + if ($headRef) { $candidates += $headRef } + $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 From 7987f5535541ba9110828f639481671241ba63dd Mon Sep 17 00:00:00 2001 From: Michal Dengusiak Date: Fri, 15 May 2026 22:39:06 +0200 Subject: [PATCH 4/9] Add SPDX + copyright header to migration-touched files The SPDX-check workflow requires the top 6 lines of every changed .cs file to contain both: // SPDX-License-Identifier: LGPL-3.0-or-later // Copyright (c) 2020-2026 Michal Dengusiak & Jakub Ziolkowski and contributors (with an en-dash between the years). Migration commits modified existing files without preserving the header. Prepend the two lines now. Co-Authored-By: Claude Opus 4.7 --- .../SAM.Core.Multitasker/Classes/MultitaskerInput.cs | 4 +++- .../SAM.Core.Multitasker/Classes/MultitaskerOutput.cs | 4 +++- .../SAM.Core.Multitasker/Classes/MultitaskerVariable.cs | 4 +++- SAM_Multitasker/SAM.Core.Multitasker/Classes/Script.cs | 4 +++- files/resources/Core/Multitasker/scripts/RunWorkflow.cs | 2 ++ .../resources/Core/Multitasker/scripts/RunWorkflow_NotSave.cs | 2 ++ files/resources/Core/Multitasker/scripts/RunWorkflow_Save.cs | 2 ++ .../Core/Multitasker/scripts/RunWorkflow_WeatherData.cs | 2 ++ files/resources/Core/Multitasker/scripts/TestScript.cs | 2 ++ 9 files changed, 22 insertions(+), 4 deletions(-) diff --git a/SAM_Multitasker/SAM.Core.Multitasker/Classes/MultitaskerInput.cs b/SAM_Multitasker/SAM.Core.Multitasker/Classes/MultitaskerInput.cs index 4db1614..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 System.Text.Json.Nodes; +// 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 diff --git a/SAM_Multitasker/SAM.Core.Multitasker/Classes/MultitaskerOutput.cs b/SAM_Multitasker/SAM.Core.Multitasker/Classes/MultitaskerOutput.cs index c66841b..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 System.Text.Json.Nodes; +// 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; diff --git a/SAM_Multitasker/SAM.Core.Multitasker/Classes/MultitaskerVariable.cs b/SAM_Multitasker/SAM.Core.Multitasker/Classes/MultitaskerVariable.cs index f70480d..db6607e 100644 --- a/SAM_Multitasker/SAM.Core.Multitasker/Classes/MultitaskerVariable.cs +++ b/SAM_Multitasker/SAM.Core.Multitasker/Classes/MultitaskerVariable.cs @@ -1,4 +1,6 @@ -using System; +// 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; diff --git a/SAM_Multitasker/SAM.Core.Multitasker/Classes/Script.cs b/SAM_Multitasker/SAM.Core.Multitasker/Classes/Script.cs index 7c92e88..48b1a93 100644 --- a/SAM_Multitasker/SAM.Core.Multitasker/Classes/Script.cs +++ b/SAM_Multitasker/SAM.Core.Multitasker/Classes/Script.cs @@ -1,4 +1,6 @@ - +// 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; diff --git a/files/resources/Core/Multitasker/scripts/RunWorkflow.cs b/files/resources/Core/Multitasker/scripts/RunWorkflow.cs index 62732de..791a6e0 100644 --- a/files/resources/Core/Multitasker/scripts/RunWorkflow.cs +++ b/files/resources/Core/Multitasker/scripts/RunWorkflow.cs @@ -1,3 +1,5 @@ +// SPDX-License-Identifier: LGPL-3.0-or-later +// Copyright (c) 2020–2026 Michal Dengusiak & Jakub Ziolkowski and contributors using SAM.Core; using SAM.Analytical; using SAM.Analytical.Tas; diff --git a/files/resources/Core/Multitasker/scripts/RunWorkflow_NotSave.cs b/files/resources/Core/Multitasker/scripts/RunWorkflow_NotSave.cs index 015886f..f23c8c2 100644 --- a/files/resources/Core/Multitasker/scripts/RunWorkflow_NotSave.cs +++ b/files/resources/Core/Multitasker/scripts/RunWorkflow_NotSave.cs @@ -1,3 +1,5 @@ +// SPDX-License-Identifier: LGPL-3.0-or-later +// Copyright (c) 2020–2026 Michal Dengusiak & Jakub Ziolkowski and contributors using SAM.Core; using SAM.Analytical; using SAM.Analytical.Tas; diff --git a/files/resources/Core/Multitasker/scripts/RunWorkflow_Save.cs b/files/resources/Core/Multitasker/scripts/RunWorkflow_Save.cs index bac322f..6c1838d 100644 --- a/files/resources/Core/Multitasker/scripts/RunWorkflow_Save.cs +++ b/files/resources/Core/Multitasker/scripts/RunWorkflow_Save.cs @@ -1,3 +1,5 @@ +// SPDX-License-Identifier: LGPL-3.0-or-later +// Copyright (c) 2020–2026 Michal Dengusiak & Jakub Ziolkowski and contributors using SAM.Core; using SAM.Analytical; using SAM.Analytical.Tas; diff --git a/files/resources/Core/Multitasker/scripts/RunWorkflow_WeatherData.cs b/files/resources/Core/Multitasker/scripts/RunWorkflow_WeatherData.cs index 5c8b6bf..2c1954c 100644 --- a/files/resources/Core/Multitasker/scripts/RunWorkflow_WeatherData.cs +++ b/files/resources/Core/Multitasker/scripts/RunWorkflow_WeatherData.cs @@ -1,3 +1,5 @@ +// SPDX-License-Identifier: LGPL-3.0-or-later +// Copyright (c) 2020–2026 Michal Dengusiak & Jakub Ziolkowski and contributors using SAM.Core; using SAM.Analytical; using SAM.Analytical.Tas; diff --git a/files/resources/Core/Multitasker/scripts/TestScript.cs b/files/resources/Core/Multitasker/scripts/TestScript.cs index 8ceb24d..70ef374 100644 --- a/files/resources/Core/Multitasker/scripts/TestScript.cs +++ b/files/resources/Core/Multitasker/scripts/TestScript.cs @@ -1,3 +1,5 @@ +// SPDX-License-Identifier: LGPL-3.0-or-later +// Copyright (c) 2020–2026 Michal Dengusiak & Jakub Ziolkowski and contributors using SAM.Core; int index = SAM.Core.Query.IndexOf("abcd", 'c'); From 57a53912713e4a1252f10b445e6d3522691f8f2a Mon Sep 17 00:00:00 2001 From: Michal Dengusiak Date: Wed, 20 May 2026 09:57:19 +0200 Subject: [PATCH 5/9] Pin System.Text.Json to 8.0.5 Aligns the repo with the SAM-BIM workspace-wide pin to the LTS-aligned System.Text.Json 8.0.5, replacing the previously pinned 10.0.8 preview. --- .../SAM.Analytical.Multitasker.csproj | 2 +- .../SAM.Core.Multitasker/SAM.Core.Multitasker.csproj | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/SAM_Multitasker/SAM.Analytical.Multitasker/SAM.Analytical.Multitasker.csproj b/SAM_Multitasker/SAM.Analytical.Multitasker/SAM.Analytical.Multitasker.csproj index 1ff330a..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/SAM.Core.Multitasker.csproj b/SAM_Multitasker/SAM.Core.Multitasker/SAM.Core.Multitasker.csproj index fbf0dd6..b81466f 100644 --- a/SAM_Multitasker/SAM.Core.Multitasker/SAM.Core.Multitasker.csproj +++ b/SAM_Multitasker/SAM.Core.Multitasker/SAM.Core.Multitasker.csproj @@ -10,7 +10,7 @@ - + From 5b6765336d44fa1bcd75e5c7baf16aff6f6b7681 Mon Sep 17 00:00:00 2001 From: Michal Dengusiak Date: Wed, 20 May 2026 13:35:32 +0200 Subject: [PATCH 6/9] fix: pin AssemblyVersion/FileVersion to 1.0.0.0 (#3) * fix: pin AssemblyVersion/FileVersion to 1.0.0.0 (drop 1.0.* wildcard) Eliminates CS1607 warnings and restores deterministic builds. Aligns with the workspace's existing fixed-version files. Scope: AssemblyInfo.cs + .csproj version attributes only. The // commented-out template example is left intact for documentation. * fix: add SPDX + copyright header to modified AssemblyInfo.cs and refresh .csproj Satisfies the spdx-check workflow which requires every changed .cs file to declare the LGPL-3.0-or-later SPDX identifier and the 2020-2026 attribution in its first 20 lines. Also updates the stale 'Copyright (c) 2020' line in .csproj files to match the SPDX header style. --- .../Properties/AssemblyInfo.cs | 8 +++++--- .../Properties/AssemblyInfo.cs | 8 +++++--- 2 files changed, 10 insertions(+), 6 deletions(-) diff --git a/Grasshopper/SAM.Analytical.Grasshopper.Multitasker/Properties/AssemblyInfo.cs b/Grasshopper/SAM.Analytical.Grasshopper.Multitasker/Properties/AssemblyInfo.cs index 7b18ef1..db0dc29 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. * @@ -53,5 +55,5 @@ // 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.*")] +[assembly: AssemblyVersion("1.0.0.0")] +[assembly: AssemblyFileVersion("1.0.0.0")] diff --git a/Grasshopper/SAM.Core.Grasshopper.Multitasker/Properties/AssemblyInfo.cs b/Grasshopper/SAM.Core.Grasshopper.Multitasker/Properties/AssemblyInfo.cs index 28816a7..8b542f1 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. * @@ -53,5 +55,5 @@ // 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.*")] +[assembly: AssemblyVersion("1.0.0.0")] +[assembly: AssemblyFileVersion("1.0.0.0")] From c14d77633f07f9a7174c68327bde4bb25e9d2d96 Mon Sep 17 00:00:00 2001 From: Michal Dengusiak Date: Wed, 20 May 2026 21:12:57 +0200 Subject: [PATCH 7/9] build: Directory.Build.props for centralised SAMVersion stamping (#4) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit * build: Directory.Build.props for centralised SAMVersion stamping Mirrors SAM-BIM/SAM#7. Stage 2 of the AssemblyVersion versioning migration. * fix: replace literal u{2013} escape with actual en-dash in SPDX header PowerShell 5.1 doesn't support backtick-u escape sequences; the apply-stage2.ps1 script leaked them as literal text in 'Copyright (c) 2020u{2013}2026'. * fix: relax SAMVersion.g.cs Target condition to != 'true' Codex P1 on SAM_LadybugTools#4: classic (non-SDK) csprojs don't set GenerateAssemblyInfo at all, so the previous '== false' condition skipped them. Switching to '!= true' catches both GenerateAssemblyInfo=false (SDK projects with legacy AssemblyInfo.cs) AND empty (classic projects). * ci: Codex P2 fixes — four-part SAMVersion, prefer head_ref for sow PRs - Append .0 so SAMVersion is 4-part (AssemblyFileVersion expects 4 parts). - Prefer github.head_ref when it matches sow/yyyy-Qx (release-promotion PRs). - Mirrors SAM sow/2026-Q2 6d87d98e. --- .github/workflows/build.yml | 40 ++++++++++++++++++- Directory.Build.props | 39 ++++++++++++++++++ .../Kernel/AssemblyInfo.cs | 5 ++- .../Properties/AssemblyInfo.cs | 3 -- ....Analytical.Grasshopper.Multitasker.csproj | 1 - .../Kernel/AssemblyInfo.cs | 5 ++- .../Properties/AssemblyInfo.cs | 3 -- .../SAM.Core.Grasshopper.Multitasker.csproj | 1 - 8 files changed, 85 insertions(+), 12 deletions(-) create mode 100644 Directory.Build.props diff --git a/.github/workflows/build.yml b/.github/workflows/build.yml index 725bc93..3ffaff9 100644 --- a/.github/workflows/build.yml +++ b/.github/workflows/build.yml @@ -2,9 +2,9 @@ name: Build (Windows) on: push: - branches: [ "master", "main", "sow/2026-Q2" ] + branches: [ "master", "main", "sow/**" ] pull_request: - branches: [ "master", "main", "sow/2026-Q2" ] + 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 @@ -56,8 +86,13 @@ jobs: foreach ($r in $deps) { if (Test-Path $r) { continue } $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) { @@ -94,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..9841655 --- /dev/null +++ b/Directory.Build.props @@ -0,0 +1,39 @@ + + + + + 1.0.0.0 + $(SAMVersion) + $(SAMVersion) + true + + + + + + <_SAMVersionLine Include="// <auto-generated />" /> + <_SAMVersionLine Include="[assembly: System.Reflection.AssemblyVersionAttribute("$(SAMVersion)")]" /> + <_SAMVersionLine Include="[assembly: System.Reflection.AssemblyFileVersionAttribute("$(SAMVersion)")]" /> + + + + + + + + + + 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 db0dc29..b2fc33a 100644 --- a/Grasshopper/SAM.Analytical.Grasshopper.Multitasker/Properties/AssemblyInfo.cs +++ b/Grasshopper/SAM.Analytical.Grasshopper.Multitasker/Properties/AssemblyInfo.cs @@ -54,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.0.0")] -[assembly: AssemblyFileVersion("1.0.0.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 8b542f1..3757f7d 100644 --- a/Grasshopper/SAM.Core.Grasshopper.Multitasker/Properties/AssemblyInfo.cs +++ b/Grasshopper/SAM.Core.Grasshopper.Multitasker/Properties/AssemblyInfo.cs @@ -54,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.0.0")] -[assembly: AssemblyFileVersion("1.0.0.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 From 59f9ece5247274c3a557de78fa2f2f0f2244e032 Mon Sep 17 00:00:00 2001 From: Michal Dengusiak Date: Thu, 21 May 2026 10:19:18 +0200 Subject: [PATCH 8/9] build: emit AssemblyInformationalVersion for non-SDK projects MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Mirrors SAM sow/2026-Q2. Adds AssemblyInformationalVersion attribute generation to Path D's SAMVersion.g.cs Target — covers Grasshopper / Tas-bridge assemblies that have GenerateAssemblyInfo=false, so they also get the CI commit SHA stamp in their ProductVersion field. SDK projects pick up InformationalVersion via PropertyGroup auto-gen as before. Local dev builds unchanged. --- Directory.Build.props | 12 +++++++++++- 1 file changed, 11 insertions(+), 1 deletion(-) diff --git a/Directory.Build.props b/Directory.Build.props index 9841655..b6b921f 100644 --- a/Directory.Build.props +++ b/Directory.Build.props @@ -1,10 +1,17 @@ - + 1.0.0.0 $(SAMVersion) $(SAMVersion) + + $(SAMVersion)+$(SAMSourceRevision) true @@ -23,6 +30,9 @@ <_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)' != ''" /> Date: Thu, 21 May 2026 12:52:04 +0200 Subject: [PATCH 9/9] build: emit AssemblyInformationalVersion for non-SDK projects MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Mirrors SAM sow/2026-Q2. Adds AssemblyInformationalVersion attribute generation to Path D's SAMVersion.g.cs Target — covers Grasshopper / Tas-bridge assemblies that have GenerateAssemblyInfo=false, so they also get the CI commit SHA stamp in their ProductVersion field. SDK projects pick up InformationalVersion via PropertyGroup auto-gen as before. Local dev builds unchanged. --- Directory.Build.props | 8 ++++++++ 1 file changed, 8 insertions(+) diff --git a/Directory.Build.props b/Directory.Build.props index b6b921f..509f4c6 100644 --- a/Directory.Build.props +++ b/Directory.Build.props @@ -12,6 +12,14 @@ that and don't overwrite. Local dev builds leave both empty -> no SHA attribute emitted. --> $(SAMVersion)+$(SAMSourceRevision) + + false true