Skip to content

Commit 6aeadee

Browse files
authored
Merge pull request #13 from link-foundation/issue-9-bef9e95e65ef
Add C# implementation with CI/CD infrastructure
2 parents 0ef1366 + 61e8980 commit 6aeadee

File tree

18 files changed

+2880
-2
lines changed

18 files changed

+2880
-2
lines changed

.github/workflows/csharp.yml

Lines changed: 324 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,324 @@
1+
name: C# CI/CD
2+
3+
on:
4+
push:
5+
branches:
6+
- main
7+
paths:
8+
- 'csharp/**'
9+
- '.github/workflows/csharp.yml'
10+
pull_request:
11+
types: [opened, synchronize, reopened]
12+
paths:
13+
- 'csharp/**'
14+
- '.github/workflows/csharp.yml'
15+
workflow_dispatch:
16+
inputs:
17+
bump_type:
18+
description: 'Version bump type'
19+
required: true
20+
type: choice
21+
options:
22+
- patch
23+
- minor
24+
- major
25+
description:
26+
description: 'Release description (optional)'
27+
required: false
28+
type: string
29+
30+
concurrency:
31+
group: ${{ github.workflow }}-${{ github.ref }}
32+
cancel-in-progress: true
33+
34+
env:
35+
DOTNET_NOLOGO: true
36+
DOTNET_CLI_TELEMETRY_OPTOUT: true
37+
DOTNET_SKIP_FIRST_TIME_EXPERIENCE: true
38+
39+
jobs:
40+
# Linting and formatting
41+
lint:
42+
name: Lint and Format Check
43+
runs-on: ubuntu-latest
44+
steps:
45+
- uses: actions/checkout@v4
46+
47+
- name: Setup .NET
48+
uses: actions/setup-dotnet@v4
49+
with:
50+
dotnet-version: '8.0.x'
51+
52+
- name: Restore dependencies
53+
working-directory: ./csharp
54+
run: dotnet restore
55+
56+
- name: Check formatting
57+
working-directory: ./csharp
58+
run: dotnet format --verify-no-changes --verbosity diagnostic
59+
60+
- name: Build with warnings as errors
61+
working-directory: ./csharp
62+
run: dotnet build --configuration Release --no-restore /warnaserror
63+
64+
# Test matrix: .NET on multiple OS
65+
test:
66+
name: Test (.NET on ${{ matrix.os }})
67+
runs-on: ${{ matrix.os }}
68+
strategy:
69+
fail-fast: false
70+
matrix:
71+
os: [ubuntu-latest, macos-latest, windows-latest]
72+
steps:
73+
- uses: actions/checkout@v4
74+
75+
- name: Setup .NET
76+
uses: actions/setup-dotnet@v4
77+
with:
78+
dotnet-version: '8.0.x'
79+
80+
- name: Restore dependencies
81+
working-directory: ./csharp
82+
run: dotnet restore
83+
84+
- name: Build
85+
working-directory: ./csharp
86+
run: dotnet build --configuration Release --no-restore
87+
88+
- name: Run tests
89+
working-directory: ./csharp
90+
run: dotnet test --configuration Release --no-build --verbosity normal --collect:"XPlat Code Coverage"
91+
92+
- name: Run example
93+
working-directory: ./csharp/examples
94+
run: dotnet run
95+
96+
- name: Upload coverage (Ubuntu only)
97+
if: matrix.os == 'ubuntu-latest'
98+
uses: codecov/codecov-action@v4
99+
with:
100+
fail_ci_if_error: false
101+
102+
# Build NuGet package
103+
build:
104+
name: Build Package
105+
runs-on: ubuntu-latest
106+
needs: [lint, test]
107+
steps:
108+
- uses: actions/checkout@v4
109+
110+
- name: Setup .NET
111+
uses: actions/setup-dotnet@v4
112+
with:
113+
dotnet-version: '8.0.x'
114+
115+
- name: Restore dependencies
116+
working-directory: ./csharp
117+
run: dotnet restore
118+
119+
- name: Build
120+
working-directory: ./csharp
121+
run: dotnet build --configuration Release --no-restore
122+
123+
- name: Pack NuGet package
124+
working-directory: ./csharp
125+
run: dotnet pack --configuration Release --no-build --output ./artifacts
126+
127+
- name: Upload artifacts
128+
uses: actions/upload-artifact@v4
129+
with:
130+
name: nuget-package
131+
path: csharp/artifacts/*.nupkg
132+
133+
# Check for changeset in PRs
134+
changeset-check:
135+
name: Changeset Check
136+
runs-on: ubuntu-latest
137+
if: github.event_name == 'pull_request'
138+
steps:
139+
- uses: actions/checkout@v4
140+
with:
141+
fetch-depth: 0
142+
143+
- name: Setup Node.js
144+
uses: actions/setup-node@v4
145+
with:
146+
node-version: '22'
147+
148+
- name: Check for changeset
149+
working-directory: ./csharp
150+
run: |
151+
# Skip changeset check for automated release PRs
152+
if [[ "${{ github.head_ref }}" == "changeset-release/"* ]] || [[ "${{ github.head_ref }}" == "changeset-manual-release-"* ]]; then
153+
echo "Skipping changeset check for automated release PR"
154+
exit 0
155+
fi
156+
157+
# Get list of changeset files (excluding README.md and config.json)
158+
CHANGESET_COUNT=$(find .changeset -name "*.md" ! -name "README.md" 2>/dev/null | wc -l)
159+
160+
# Get changed files in PR
161+
CHANGED_FILES=$(git diff --name-only origin/${{ github.base_ref }}...HEAD)
162+
163+
# Check if any source files changed (excluding docs and config)
164+
SOURCE_CHANGED=$(echo "$CHANGED_FILES" | grep -E "^csharp/(src/|tests/|examples/)" | wc -l)
165+
166+
if [ "$SOURCE_CHANGED" -gt 0 ] && [ "$CHANGESET_COUNT" -eq 0 ]; then
167+
echo "::warning::No changeset found. Please add a changeset in csharp/.changeset/"
168+
echo ""
169+
echo "To create a changeset:"
170+
echo " cd csharp/.changeset"
171+
echo " Create a file: YYYYMMDD_HHMMSS_description.md"
172+
echo ""
173+
echo "See csharp/.changeset/README.md for more information."
174+
# Note: This is a warning, not a failure, to allow flexibility
175+
exit 0
176+
fi
177+
178+
echo "✓ Changeset check passed"
179+
180+
# Automatic release on push to main (if version changed)
181+
auto-release:
182+
name: Auto Release
183+
needs: [lint, test, build]
184+
if: github.event_name == 'push' && github.ref == 'refs/heads/main'
185+
runs-on: ubuntu-latest
186+
permissions:
187+
contents: write
188+
packages: write
189+
steps:
190+
- uses: actions/checkout@v4
191+
with:
192+
fetch-depth: 0
193+
194+
- name: Setup .NET
195+
uses: actions/setup-dotnet@v4
196+
with:
197+
dotnet-version: '8.0.x'
198+
199+
- name: Setup Node.js
200+
uses: actions/setup-node@v4
201+
with:
202+
node-version: '22'
203+
204+
- name: Check if version changed
205+
id: version_check
206+
working-directory: ./csharp
207+
run: |
208+
# Get current version from csproj
209+
CURRENT_VERSION=$(grep -Po '(?<=<Version>)[^<]*' src/Lino.Objects.Codec/Lino.Objects.Codec.csproj)
210+
echo "current_version=$CURRENT_VERSION" >> $GITHUB_OUTPUT
211+
212+
# Check if tag exists
213+
if git rev-parse "csharp-v$CURRENT_VERSION" >/dev/null 2>&1; then
214+
echo "Tag csharp-v$CURRENT_VERSION already exists, skipping release"
215+
echo "should_release=false" >> $GITHUB_OUTPUT
216+
else
217+
echo "New version detected: $CURRENT_VERSION"
218+
echo "should_release=true" >> $GITHUB_OUTPUT
219+
fi
220+
221+
- name: Download artifacts
222+
if: steps.version_check.outputs.should_release == 'true'
223+
uses: actions/download-artifact@v4
224+
with:
225+
name: nuget-package
226+
path: csharp/artifacts/
227+
228+
- name: Publish to NuGet
229+
if: steps.version_check.outputs.should_release == 'true'
230+
working-directory: ./csharp
231+
env:
232+
NUGET_API_KEY: ${{ secrets.NUGET_API_KEY }}
233+
run: |
234+
if [ -z "$NUGET_API_KEY" ]; then
235+
echo "::warning::NUGET_API_KEY not set, skipping publish to NuGet"
236+
else
237+
dotnet nuget push artifacts/*.nupkg \
238+
--api-key "$NUGET_API_KEY" \
239+
--source https://api.nuget.org/v3/index.json \
240+
--skip-duplicate
241+
fi
242+
243+
- name: Create GitHub Release
244+
if: steps.version_check.outputs.should_release == 'true'
245+
env:
246+
GH_TOKEN: ${{ secrets.GITHUB_TOKEN }}
247+
working-directory: ./csharp
248+
run: |
249+
node scripts/create-github-release.mjs \
250+
--version "${{ steps.version_check.outputs.current_version }}" \
251+
--repository "${{ github.repository }}" \
252+
--tag-prefix "csharp-v"
253+
254+
# Manual release via workflow_dispatch
255+
manual-release:
256+
name: Manual Release
257+
needs: [lint, test, build]
258+
if: github.event_name == 'workflow_dispatch'
259+
runs-on: ubuntu-latest
260+
permissions:
261+
contents: write
262+
packages: write
263+
steps:
264+
- uses: actions/checkout@v4
265+
with:
266+
fetch-depth: 0
267+
token: ${{ secrets.GITHUB_TOKEN }}
268+
269+
- name: Setup .NET
270+
uses: actions/setup-dotnet@v4
271+
with:
272+
dotnet-version: '8.0.x'
273+
274+
- name: Setup Node.js
275+
uses: actions/setup-node@v4
276+
with:
277+
node-version: '22'
278+
279+
- name: Configure git
280+
run: |
281+
git config user.name "github-actions[bot]"
282+
git config user.email "github-actions[bot]@users.noreply.github.com"
283+
284+
- name: Version and commit
285+
id: version
286+
working-directory: ./csharp
287+
run: |
288+
node scripts/version-and-commit.mjs \
289+
--bump-type "${{ github.event.inputs.bump_type }}" \
290+
--description "${{ github.event.inputs.description }}"
291+
292+
- name: Build release
293+
if: steps.version.outputs.version_committed == 'true' || steps.version.outputs.already_released == 'true'
294+
working-directory: ./csharp
295+
run: |
296+
dotnet restore
297+
dotnet build --configuration Release
298+
dotnet pack --configuration Release --output ./artifacts
299+
300+
- name: Publish to NuGet
301+
if: steps.version.outputs.version_committed == 'true' || steps.version.outputs.already_released == 'true'
302+
working-directory: ./csharp
303+
env:
304+
NUGET_API_KEY: ${{ secrets.NUGET_API_KEY }}
305+
run: |
306+
if [ -z "$NUGET_API_KEY" ]; then
307+
echo "::warning::NUGET_API_KEY not set, skipping publish to NuGet"
308+
else
309+
dotnet nuget push artifacts/*.nupkg \
310+
--api-key "$NUGET_API_KEY" \
311+
--source https://api.nuget.org/v3/index.json \
312+
--skip-duplicate
313+
fi
314+
315+
- name: Create GitHub Release
316+
if: steps.version.outputs.version_committed == 'true' || steps.version.outputs.already_released == 'true'
317+
env:
318+
GH_TOKEN: ${{ secrets.GITHUB_TOKEN }}
319+
working-directory: ./csharp
320+
run: |
321+
node scripts/create-github-release.mjs \
322+
--version "${{ steps.version.outputs.new_version }}" \
323+
--repository "${{ github.repository }}" \
324+
--tag-prefix "csharp-v"

0 commit comments

Comments
 (0)