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
112 changes: 112 additions & 0 deletions .github/workflows/ci.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,112 @@
name: CI

on:
pull_request:
push:
branches:
- master
- main
- "codex/**"
workflow_dispatch:

permissions:
contents: read

env:
CONFIGURATION: Release
CORE_PROJECT: Evaluant.Calculator/NCalc.csproj
TEST_PROJECT: Evaluant.Calculator.Tests/NCalc.Tests.csproj
DOTNET_NOLOGO: true
NCALC_EXPECTED_PUBLIC_KEY_TOKEN: f59bbff0fd43f598

jobs:
quality-gates:
name: Local quality gates and compatibility
runs-on: ubuntu-latest

steps:
- name: Check out repository
uses: actions/checkout@v4

- name: Set up .NET SDK
uses: actions/setup-dotnet@v4
with:
dotnet-version: 10.0.x

- name: Install Mono tooling
run: |
sudo apt-get update
sudo apt-get install --yes mono-devel mono-utils

- name: Prepare strong-name key
shell: bash
env:
NCALC_STRONG_NAME_KEY_BASE64: ${{ secrets.NCALC_STRONG_NAME_KEY_BASE64 }}
run: |
set -euo pipefail

key_path="$RUNNER_TEMP/NCalc.snk"

if [ -n "${NCALC_STRONG_NAME_KEY_BASE64:-}" ]; then
printf '%s' "$NCALC_STRONG_NAME_KEY_BASE64" | base64 --decode > "$key_path"
else
sn -k "$key_path"
sn -p "$key_path" "$RUNNER_TEMP/NCalc.public.snk" >/dev/null
token="$(sn -tp "$RUNNER_TEMP/NCalc.public.snk" | awk '/Public Key Token/ { print $4 }')"
echo "NCALC_EXPECTED_PUBLIC_KEY_TOKEN=$token" >> "$GITHUB_ENV"
echo "Using an ephemeral strong-name key for non-release CI validation."
fi

echo "NCalcStrongNameKeyFile=$key_path" >> "$GITHUB_ENV"

- name: Run local validation gates
run: scripts/validate.sh

build-test:
name: Restore, build, and test (${{ matrix.os }})
runs-on: ${{ matrix.os }}
strategy:
fail-fast: false
matrix:
os:
- ubuntu-latest
- macos-latest
- windows-latest

steps:
- name: Check out repository
uses: actions/checkout@v4

- name: Set up .NET SDK
uses: actions/setup-dotnet@v4
with:
dotnet-version: 10.0.x

- name: Show .NET information
run: dotnet --info

- name: Restore
run: |
dotnet restore ${{ env.CORE_PROJECT }}
dotnet restore ${{ env.TEST_PROJECT }}

- name: Build
run: |
dotnet build ${{ env.CORE_PROJECT }} --configuration ${{ env.CONFIGURATION }} --no-restore
dotnet build ${{ env.TEST_PROJECT }} --configuration ${{ env.CONFIGURATION }} --no-restore

- name: Test
run: >
dotnet test ${{ env.TEST_PROJECT }}
--configuration ${{ env.CONFIGURATION }}
--no-build
--logger "trx;LogFileName=test-results.trx"
--results-directory TestResults

- name: Upload test results
if: always()
uses: actions/upload-artifact@v4
with:
name: test-results-${{ matrix.os }}
path: TestResults
if-no-files-found: ignore
192 changes: 192 additions & 0 deletions .github/workflows/release.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,192 @@
name: Release Package

on:
release:
types:
- published
workflow_dispatch:
inputs:
package_version:
description: NuGet package version to build, without a leading v.
required: true
type: string
publish:
description: Publish the package to NuGet.org instead of running a dry pack.
required: true
default: false
type: boolean

permissions:
contents: read

env:
CONFIGURATION: Release
CORE_PROJECT: Evaluant.Calculator/NCalc.csproj
TEST_PROJECT: Evaluant.Calculator.Tests/NCalc.Tests.csproj
DOTNET_NOLOGO: true
NCALC_EXPECTED_PUBLIC_KEY_TOKEN: f59bbff0fd43f598

jobs:
package:
name: Build, test, and pack
runs-on: ubuntu-latest
outputs:
package-version: ${{ steps.resolve-package-version.outputs.package-version }}

steps:
- name: Check out repository
uses: actions/checkout@v4

- name: Set up .NET SDK
uses: actions/setup-dotnet@v4
with:
dotnet-version: 10.0.x

- name: Resolve package version
id: resolve-package-version
shell: bash
run: |
if [ "$GITHUB_EVENT_NAME" = "release" ]; then
package_version="${GITHUB_REF_NAME#v}"
else
package_version="${{ inputs.package_version }}"
fi

if [ -z "$package_version" ]; then
echo "Package version could not be resolved." >&2
exit 1
fi

if [[ ! "$package_version" =~ ^([0-9]+)\.([0-9]+)\.([0-9]+)([-+][0-9A-Za-z.-]+)?$ ]]; then
echo "Unsupported package version format: $package_version" >&2
exit 2
fi

package_major_version="${BASH_REMATCH[1]}"
if [ "$package_major_version" -lt 2 ]; then
echo "NCalc uses a new strong-name key, so release package versions must be 2.0.0 or later." >&2
exit 1
fi

echo "PACKAGE_VERSION=$package_version" >> "$GITHUB_ENV"
echo "package-version=$package_version" >> "$GITHUB_OUTPUT"

- name: Install Mono tooling
run: |
sudo apt-get update
sudo apt-get install --yes mono-devel mono-utils

- name: Prepare release strong-name key
shell: bash
env:
NCALC_STRONG_NAME_KEY_BASE64: ${{ secrets.NCALC_STRONG_NAME_KEY_BASE64 }}
run: |
set -euo pipefail

if [ -z "${NCALC_STRONG_NAME_KEY_BASE64:-}" ]; then
echo "NCALC_STRONG_NAME_KEY_BASE64 is required for release packaging." >&2
exit 2
fi

key_path="$RUNNER_TEMP/NCalc.snk"
public_key_path="$RUNNER_TEMP/NCalc.public.snk"
printf '%s' "$NCALC_STRONG_NAME_KEY_BASE64" | base64 --decode > "$key_path"
sn -p "$key_path" "$public_key_path" >/dev/null

actual_token="$(sn -tp "$public_key_path" | awk '/Public Key Token/ { print $4 }')"
if [ "$actual_token" != "$NCALC_EXPECTED_PUBLIC_KEY_TOKEN" ]; then
echo "Release signing key token mismatch: expected $NCALC_EXPECTED_PUBLIC_KEY_TOKEN, got $actual_token." >&2
exit 1
fi

echo "NCalcStrongNameKeyFile=$key_path" >> "$GITHUB_ENV"

- name: Run release validation gates
run: scripts/validate.sh

- name: Upload NuGet package
uses: actions/upload-artifact@v4
with:
name: ncalc-${{ steps.resolve-package-version.outputs.package-version }}
path: |
artifacts/packages/*.nupkg
artifacts/packages/*.snupkg
if-no-files-found: error

windows-framework-smoke:
name: Windows .NET Framework package smoke
needs: package
runs-on: windows-latest

steps:
- name: Check out repository
uses: actions/checkout@v4

- name: Set up .NET SDK
uses: actions/setup-dotnet@v4
with:
dotnet-version: 10.0.x

- name: Download NuGet package
uses: actions/download-artifact@v4
with:
name: ncalc-${{ needs.package.outputs.package-version }}
path: artifacts/packages

- name: Run .NET Framework consumer smoke
shell: pwsh
run: >
scripts/windows-framework-consumer-smoke.ps1
-PackageSource artifacts/packages
-PackageVersion "${{ needs.package.outputs.package-version }}"

publish:
name: Publish to NuGet.org
needs:
- package
- windows-framework-smoke
runs-on: ubuntu-latest

steps:
- name: Set up .NET SDK
uses: actions/setup-dotnet@v4
with:
dotnet-version: 10.0.x

- name: Download NuGet package
uses: actions/download-artifact@v4
with:
name: ncalc-${{ needs.package.outputs.package-version }}
path: artifacts/packages

- name: Publish to NuGet.org
shell: bash
run: |
should_publish=false

if [ "$GITHUB_EVENT_NAME" = "release" ]; then
should_publish=true
elif [ "${{ inputs.publish }}" = "true" ]; then
should_publish=true
fi

if [ "$should_publish" != "true" ]; then
echo "Dry run complete. Package was built but not published."
exit 0
fi

for package in artifacts/packages/*.nupkg; do
dotnet nuget push "$package" \
--api-key "${{ secrets.NUGET_API_KEY }}" \
--source "https://api.nuget.org/v3/index.json" \
--skip-duplicate
done

for symbols_package in artifacts/packages/*.snupkg; do
if [ -e "$symbols_package" ]; then
dotnet nuget push "$symbols_package" \
--api-key "${{ secrets.NUGET_API_KEY }}" \
--source "https://api.nuget.org/v3/index.json" \
--skip-duplicate
fi
done
51 changes: 51 additions & 0 deletions .gitignore
Original file line number Diff line number Diff line change
@@ -0,0 +1,51 @@
# Build outputs
[Bb]in/
[Oo]bj/
[Bb]uild/
!build/
!build/NCalc.BuildTools/
!build/NCalc.BuildTools/*.cs
!build/NCalc.BuildTools/*.csproj
build/NCalc.BuildTools/[Bb]in/
build/NCalc.BuildTools/[Oo]bj/
artifacts/
TestResults/
_Package/

# User and IDE state
.vs/
.idea/
*.user
*.suo
*.userosscache
*.sln.docstates
*.sln.cache
*.rsuser
*ReSharper*/

# Test and coverage output
*.trx
*.coverage
*.coveragexml
*.dotCover
*.itrace.csdef

# NuGet outputs
*.nupkg
*.snupkg
packages/

# Private signing material
*.snk
*.pfx
*.p12
*.pem

# Patch and merge leftovers
*.patch
*.orig
*.rej

# OS files
.DS_Store
desktop.ini
29 changes: 29 additions & 0 deletions Directory.Build.props
Original file line number Diff line number Diff line change
@@ -0,0 +1,29 @@
<Project>
<PropertyGroup>
<Deterministic>true</Deterministic>
<ContinuousIntegrationBuild Condition="'$(GITHUB_ACTIONS)' == 'true'">true</ContinuousIntegrationBuild>
</PropertyGroup>

<PropertyGroup Condition="'$(MSBuildProjectName)' == 'NCalc'">
<PackageId>NCalc</PackageId>
<Title>NCalc</Title>
<Authors>Sebastien Ros</Authors>
<Product>NCalc</Product>
<Description>Mathematical expressions evaluator for .NET.</Description>
<PackageTags>ncalc;expression;expressions;calculator;formula;evaluator</PackageTags>
<PackageProjectUrl>https://github.com/sheetsync/NCalc</PackageProjectUrl>
<PackageLicenseExpression>MIT</PackageLicenseExpression>
<RepositoryUrl>https://github.com/sheetsync/NCalc.git</RepositoryUrl>
<RepositoryType>git</RepositoryType>
<PackageReadmeFile>README.md</PackageReadmeFile>
<PackageRequireLicenseAcceptance>false</PackageRequireLicenseAcceptance>
<IncludeSymbols>true</IncludeSymbols>
<SymbolPackageFormat>snupkg</SymbolPackageFormat>
<PublishRepositoryUrl>true</PublishRepositoryUrl>
<EmbedUntrackedSources>true</EmbedUntrackedSources>
</PropertyGroup>

<ItemGroup Condition="'$(UsingMicrosoftNETSdk)' == 'true' and '$(MSBuildProjectName)' == 'NCalc'">
<PackageReference Include="Microsoft.SourceLink.GitHub" Version="8.0.0" PrivateAssets="all" />
</ItemGroup>
</Project>
5 changes: 5 additions & 0 deletions Directory.Build.targets
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
<Project>
<ItemGroup Condition="'$(MSBuildProjectName)' == 'NCalc' and Exists('$(MSBuildThisFileDirectory)README.md')">
<None Include="$(MSBuildThisFileDirectory)README.md" Link="README.md" Pack="true" PackagePath="/" />
</ItemGroup>
</Project>
Loading
Loading