Skip to content
Open
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
61 changes: 57 additions & 4 deletions .github/workflows/build.yml
Original file line number Diff line number Diff line change
Expand Up @@ -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:
Expand Down Expand Up @@ -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
Expand All @@ -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

Expand All @@ -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"
)
Expand Down
81 changes: 63 additions & 18 deletions .github/workflows/spdx-check.yml
Original file line number Diff line number Diff line change
Expand Up @@ -2,44 +2,89 @@ name: SPDX + Copyright header check

on:
pull_request:
workflow_dispatch:

jobs:
spdx:
runs-on: ubuntu-latest
permissions:
contents: read
pull-requests: read

steps:
- uses: actions/checkout@v4
- name: Checkout repository
uses: actions/checkout@v5
with:
fetch-depth: 0

- name: Check header in changed .cs files
shell: bash
run: |
set -e
BASE="${{ github.event.pull_request.base.sha }}"
HEAD="${{ github.event.pull_request.head.sha }}"
set -euo pipefail

if [ "${{ github.event_name }}" = "pull_request" ]; then
BASE="${{ github.event.pull_request.base.sha }}"
HEAD="${{ github.event.pull_request.head.sha }}"
else
HEAD="${{ github.sha }}"
BASE="$(git rev-parse "${HEAD}^" 2>/dev/null || true)"
fi

FILES=$(git diff --name-only "$BASE" "$HEAD" -- '*.cs' || true)
echo "Base SHA: ${BASE:-<none>}"
echo "Head SHA: $HEAD"
echo

if [ -z "$FILES" ]; then
if [ -z "${BASE:-}" ]; then
mapfile -t files < <(git ls-files '*.cs')
else
mapfile -t files < <(git diff --diff-filter=ACMR --name-only "$BASE" "$HEAD" -- '*.cs' || true)
fi

if [ "${#files[@]}" -eq 0 ]; then
echo "No C# files changed."
exit 0
fi

MISSING=""
for f in $FILES; do
HEADBLOCK=$(head -n 6 "$f")
echo "Changed C# files:"
printf ' - %s\n' "${files[@]}"
echo

missing=()

for f in "${files[@]}"; do
if [ ! -f "$f" ]; then
echo "Skipping missing file: $f"
continue
fi

headblock="$(head -n 20 "$f" | sed '1s/^\xEF\xBB\xBF//' | tr -d '\r')"

echo "Checking: $f"

if ! grep -q "SPDX-License-Identifier: LGPL-3.0-or-later" <<< "$headblock"; then
echo " Missing SPDX line"
missing+=("$f")
continue
fi

if ! grep -qE "Copyright \(c\) 2020[-–]2026 Michal Dengusiak & Jakub Ziolkowski and contributors" <<< "$headblock"; then
echo " Missing copyright line"
missing+=("$f")
continue
fi

echo "$HEADBLOCK" | grep -q "// SPDX-License-Identifier: LGPL-3.0-or-later" || MISSING="$MISSING $f"
echo "$HEADBLOCK" | grep -q "// Copyright (c) 2020–2026 Michal Dengusiak & Jakub Ziolkowski and contributors" || MISSING="$MISSING $f"
echo " OK"
done

if [ -n "$MISSING" ]; then
echo "❌ Missing required header in:"
for f in $MISSING; do echo " - $f"; done
echo ""
echo "Each changed .cs file must start with:"
echo
if [ "${#missing[@]}" -gt 0 ]; then
echo "Missing required header in:"
printf ' - %s\n' "${missing[@]}"
echo
echo "Each checked .cs file must contain within the first 20 lines:"
echo "// SPDX-License-Identifier: LGPL-3.0-or-later"
echo "// Copyright (c) 20202026 Michal Dengusiak & Jakub Ziolkowski and contributors"
echo "// Copyright (c) 2020-2026 Michal Dengusiak & Jakub Ziolkowski and contributors"
exit 1
fi

echo "SPDX + copyright headers OK."
echo "SPDX + copyright headers OK."
57 changes: 57 additions & 0 deletions Directory.Build.props
Original file line number Diff line number Diff line change
@@ -0,0 +1,57 @@
<Project>

<PropertyGroup>
<!-- Local dev builds default to 1.0.0.0. CI passes /p:SAMVersion=YYYY.Q.<run_number>.0. -->
<SAMVersion Condition="'$(SAMVersion)' == ''">1.0.0.0</SAMVersion>
<AssemblyVersion>$(SAMVersion)</AssemblyVersion>
<FileVersion>$(SAMVersion)</FileVersion>
<!--
InformationalVersion: the content-identity stamp (SemVer +build metadata).
Composed from SAMVersion + SAMSourceRevision when CI sets the latter. If CI already
set InformationalVersion directly (e.g. SAM_Deploy installer.yml does this), respect
that and don't overwrite. Local dev builds leave both empty -> no SHA attribute emitted.
-->
<InformationalVersion Condition="'$(InformationalVersion)' == '' AND '$(SAMSourceRevision)' != ''">$(SAMVersion)+$(SAMSourceRevision)</InformationalVersion>
<!--
Suppress the SDK's SourceLink auto-append behaviour. By default, when SourceLink is
configured on a project (via NuGet or repo settings), the SDK appends the project's
OWN commit SHA to InformationalVersion (e.g. '2026.2.164.0+998af06.dd02a4c2...').
We set InformationalVersion explicitly to SAM_Deploy's SHA (which encodes all 22
submodule pointers), so the SDK's extra append is noise. False = keep our value clean.
-->
<IncludeSourceRevisionInInformationalVersion>false</IncludeSourceRevisionInInformationalVersion>
<Deterministic>true</Deterministic>
</PropertyGroup>

<!--
Projects where SDK assembly-attribute generation is disabled (legacy AssemblyInfo.cs
with GenerateAssemblyInfo=false) OR projects where the SDK doesn't auto-generate
attributes at all (classic-style csprojs that don't set the property — '' evaluates
as not 'true') ignore the AssemblyVersion/FileVersion MSBuild properties above.
Inject the attributes via a generated source file so those assemblies also get
stamped by CI.
-->
<Target Name="_GenerateSAMVersionFile"
BeforeTargets="CoreCompile"
Condition="'$(GenerateAssemblyInfo)' != 'true'">
<ItemGroup>
<_SAMVersionLine Include="// &lt;auto-generated /&gt;" />
<_SAMVersionLine Include="[assembly: System.Reflection.AssemblyVersionAttribute(&quot;$(SAMVersion)&quot;)]" />
<_SAMVersionLine Include="[assembly: System.Reflection.AssemblyFileVersionAttribute(&quot;$(SAMVersion)&quot;)]" />
<!-- Only emit InformationalVersion attribute when CI provided a value (SDK projects
would otherwise get it via PropertyGroup -> SDK auto-gen). -->
<_SAMVersionLine Include="[assembly: System.Reflection.AssemblyInformationalVersionAttribute(&quot;$(InformationalVersion)&quot;)]" Condition="'$(InformationalVersion)' != ''" />
</ItemGroup>
<MakeDir Directories="$(IntermediateOutputPath)" />
<WriteLinesToFile
File="$(IntermediateOutputPath)SAMVersion.g.cs"
Lines="@(_SAMVersionLine)"
Overwrite="true"
WriteOnlyWhenDifferent="true" />
<ItemGroup>
<Compile Include="$(IntermediateOutputPath)SAMVersion.g.cs" KeepDuplicates="false" />
<FileWrites Include="$(IntermediateOutputPath)SAMVersion.g.cs" />
</ItemGroup>
</Target>

</Project>
Original file line number Diff line number Diff line change
@@ -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;

Expand Down
Original file line number Diff line number Diff line change
@@ -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.
*
Expand Down Expand Up @@ -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.*")]
Original file line number Diff line number Diff line change
Expand Up @@ -10,7 +10,6 @@
<GenerateAssemblyInfo>false</GenerateAssemblyInfo>
<UseWindowsForms>true</UseWindowsForms>
<ImportWindowsDesktopTargets>true</ImportWindowsDesktopTargets>
<Deterministic>false</Deterministic>
<AppendTargetFrameworkToOutputPath>false</AppendTargetFrameworkToOutputPath>
<AppendRuntimeIdentifierToOutputPath>false</AppendRuntimeIdentifierToOutputPath>
</PropertyGroup>
Expand Down Expand Up @@ -60,9 +59,7 @@
<ExcludeAssets>runtime</ExcludeAssets>
</PackageReference>
<PackageReference Include="Microsoft.CSharp" Version="4.7.0" />
<PackageReference Include="Newtonsoft.Json">
<Version>13.0.3</Version>
</PackageReference>
<PackageReference Include="System.Text.Json" Version="8.0.5" />
<PackageReference Include="System.Drawing.Common" Version="7.0.0" />
</ItemGroup>
<ItemGroup>
Expand Down
Original file line number Diff line number Diff line change
@@ -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 SAM.Core.Mollier;

namespace SAM.Core.Grasshopper.Mollier
Expand All @@ -18,9 +20,9 @@ public MollierChartObject(IUIMollierObject uIMollierObject, ChartType chartType,
this.z = z;
}

public MollierChartObject(JObject jObject)
public MollierChartObject(JsonObject jObject)
{
FromJObject(jObject);
FromJsonObject(jObject);
}

public MollierChartObject(MollierChartObject mollierChartObject)
Expand Down Expand Up @@ -65,7 +67,7 @@ public IUIMollierAppearance UIMollierAppearance
}
}

public bool FromJObject(JObject jObject)
public bool FromJsonObject(JsonObject jObject)
{
if (jObject == null)
{
Expand All @@ -74,30 +76,30 @@ public bool FromJObject(JObject jObject)

if (jObject.ContainsKey("UIMollierObject"))
{
uIMollierObject = Core.Query.IJSAMObject< IUIMollierObject >(jObject.Value<JObject>("UIMollierObject"));
uIMollierObject = Core.Query.IJSAMObject< IUIMollierObject >(jObject["UIMollierObject"] as JsonObject);
}

if (jObject.ContainsKey("ChartType"))
{
chartType = Core.Query.Enum<ChartType>(jObject.Value<string>("ChartType"));
chartType = Core.Query.Enum<ChartType>(jObject["ChartType"]?.GetValue<string>() ?? null);
}

if (jObject.ContainsKey("Z"))
{
z = jObject.Value<double>("Z");
z = jObject["Z"]?.GetValue<double>() ?? default(double);
}

return true;
}

public JObject ToJObject()
public JsonObject ToJsonObject()
{
JObject jObject = new JObject();
JsonObject jObject = new JsonObject();
jObject.Add("_type", Core.Query.FullTypeName(this));

if(uIMollierObject != null)
{
jObject.Add("UIMollierObject", uIMollierObject.ToJObject());
jObject.Add("UIMollierObject", uIMollierObject.ToJsonObject());
}

jObject.Add("ChartType", chartType.ToString());
Expand Down
Original file line number Diff line number Diff line change
@@ -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;

Expand Down
Original file line number Diff line number Diff line change
@@ -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.
*
Expand Down Expand Up @@ -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.*")]
Loading