1- name : C#
1+ name : C# CI/CD Pipeline
22
3- # Trigger the workflow when a new tag is pushed
43on :
54 push :
6- tags :
7- - ' v*' # This example triggers on tags like v1.0.0
5+ branches :
6+ - main
7+ paths :
8+ - ' csharp/**'
9+ - ' scripts/**'
10+ - ' .github/workflows/csharp.yml'
11+ pull_request :
12+ types : [opened, synchronize, reopened]
13+ paths :
14+ - ' csharp/**'
15+ - ' scripts/**'
16+ - ' .github/workflows/csharp.yml'
17+ workflow_dispatch :
18+ inputs :
19+ release_mode :
20+ description : ' Release mode'
21+ required : true
22+ type : choice
23+ default : ' instant'
24+ options :
25+ - instant
26+ - changeset-pr
27+ bump_type :
28+ description : ' Version bump type'
29+ required : true
30+ type : choice
31+ options :
32+ - patch
33+ - minor
34+ - major
35+ description :
36+ description : ' Release description (optional)'
37+ required : false
38+ type : string
39+
40+ concurrency :
41+ group : csharp-${{ github.workflow }}-${{ github.ref }}
42+ cancel-in-progress : true
43+
44+ env :
45+ DOTNET_SKIP_FIRST_TIME_EXPERIENCE : true
46+ DOTNET_CLI_TELEMETRY_OPTOUT : true
47+ DOTNET_NOLOGO : true
48+
49+ defaults :
50+ run :
51+ working-directory : csharp
852
953jobs :
10- build-and-publish :
54+ # === DETECT CHANGES - determines which jobs should run ===
55+ detect-changes :
56+ name : Detect Changes
57+ runs-on : ubuntu-latest
58+ if : github.event_name != 'workflow_dispatch'
59+ outputs :
60+ cs-changed : ${{ steps.changes.outputs.cs-changed }}
61+ csproj-changed : ${{ steps.changes.outputs.csproj-changed }}
62+ sln-changed : ${{ steps.changes.outputs.sln-changed }}
63+ props-changed : ${{ steps.changes.outputs.props-changed }}
64+ mjs-changed : ${{ steps.changes.outputs.mjs-changed }}
65+ docs-changed : ${{ steps.changes.outputs.docs-changed }}
66+ workflow-changed : ${{ steps.changes.outputs.workflow-changed }}
67+ any-code-changed : ${{ steps.changes.outputs.any-code-changed }}
68+ csharp-code-changed : ${{ steps.changes.outputs.csharp-code-changed }}
69+ steps :
70+ - uses : actions/checkout@v4
71+ with :
72+ fetch-depth : 0
73+
74+ - name : Setup Node.js
75+ uses : actions/setup-node@v4
76+ with :
77+ node-version : ' 20.x'
78+
79+ - name : Detect changes
80+ id : changes
81+ working-directory : .
82+ env :
83+ GITHUB_EVENT_NAME : ${{ github.event_name }}
84+ GITHUB_BASE_SHA : ${{ github.event.pull_request.base.sha }}
85+ GITHUB_HEAD_SHA : ${{ github.event.pull_request.head.sha }}
86+ run : node scripts/detect-code-changes.mjs
87+
88+ # === CHANGESET CHECK - only runs on PRs with code changes ===
89+ changeset-check :
90+ name : Changeset Validation
1191 runs-on : ubuntu-latest
92+ needs : [detect-changes]
93+ if : github.event_name == 'pull_request' && needs.detect-changes.outputs.csharp-code-changed == 'true'
94+ steps :
95+ - uses : actions/checkout@v4
96+ with :
97+ fetch-depth : 0
98+
99+ - name : Setup Node.js
100+ uses : actions/setup-node@v4
101+ with :
102+ node-version : ' 20.x'
103+
104+ - name : Validate changeset
105+ working-directory : .
106+ env :
107+ GITHUB_BASE_REF : ${{ github.base_ref }}
108+ GITHUB_BASE_SHA : ${{ github.event.pull_request.base.sha }}
109+ GITHUB_HEAD_SHA : ${{ github.event.pull_request.head.sha }}
110+ run : |
111+ # Skip changeset check for automated release PRs
112+ if [[ "${{ github.head_ref }}" == "changeset-release/"* ]] || [[ "${{ github.head_ref }}" == "changeset-manual-release-"* ]]; then
113+ echo "Skipping changeset check for automated release PR"
114+ exit 0
115+ fi
116+
117+ # Check if changeset exists in csharp/.changeset
118+ CHANGESET_COUNT=$(find csharp/.changeset -name "*.md" ! -name "README.md" 2>/dev/null | wc -l)
119+ if [ "$CHANGESET_COUNT" -eq 0 ]; then
120+ echo "::warning::No changeset found in csharp/.changeset/. Please add a changeset for C# code changes."
121+ else
122+ echo "Found $CHANGESET_COUNT changeset file(s)"
123+ fi
124+
125+ # === LINT AND FORMAT CHECK ===
126+ lint :
127+ name : Lint and Format Check
128+ runs-on : ubuntu-latest
129+ needs : [detect-changes]
130+ if : |
131+ github.event_name == 'push' ||
132+ github.event_name == 'workflow_dispatch' ||
133+ needs.detect-changes.outputs.cs-changed == 'true' ||
134+ needs.detect-changes.outputs.csproj-changed == 'true' ||
135+ needs.detect-changes.outputs.sln-changed == 'true' ||
136+ needs.detect-changes.outputs.props-changed == 'true'
137+ steps :
138+ - uses : actions/checkout@v4
12139
140+ - name : Setup .NET
141+ uses : actions/setup-dotnet@v4
142+ with :
143+ dotnet-version : ' 8.0.x'
144+
145+ - name : Setup Node.js
146+ uses : actions/setup-node@v4
147+ with :
148+ node-version : ' 20.x'
149+
150+ - name : Restore dependencies
151+ run : dotnet restore
152+
153+ - name : Check formatting
154+ run : dotnet format --verify-no-changes --verbosity diagnostic
155+
156+ - name : Build with warnings as errors
157+ run : dotnet build --no-restore --configuration Release /warnaserror
158+
159+ - name : Check file size limit
160+ working-directory : .
161+ run : node scripts/check-file-size.mjs --lang csharp
162+
163+ # === TEST ON MULTIPLE OS ===
164+ test :
165+ name : Test (${{ matrix.os }})
166+ runs-on : ${{ matrix.os }}
167+ needs : [detect-changes, changeset-check]
168+ if : always() && (github.event_name == 'push' || github.event_name == 'workflow_dispatch' || needs.changeset-check.result == 'success' || needs.changeset-check.result == 'skipped')
169+ strategy :
170+ fail-fast : false
171+ matrix :
172+ os : [ubuntu-latest, macos-latest, windows-latest]
13173 steps :
14- # Step 1: Checkout the repository
15- - name : Checkout repository
16- uses : actions/checkout@v3
174+ - uses : actions/checkout@v4
17175
18- # Step 2: Setup .NET SDK
19176 - name : Setup .NET
20- uses : actions/setup-dotnet@v3
177+ uses : actions/setup-dotnet@v4
21178 with :
22- dotnet-version : ' 8.0.x' # Updated to match your project
179+ dotnet-version : ' 8.0.x'
23180
24- # Step 3: Restore dependencies
25181 - name : Restore dependencies
26182 run : dotnet restore
27183
28- # Step 4: Build the project
29184 - name : Build
30- run : dotnet build --configuration Release -- no-restore
185+ run : dotnet build --no-restore --configuration Release
31186
32- # Step 5: Run tests (optional)
33- - name : Test
34- run : dotnet test --verbosity normal
187+ - name : Run tests
188+ run : dotnet test --no-build --configuration Release --verbosity normal --collect:"XPlat Code Coverage"
189+
190+ - name : Upload coverage to Codecov
191+ if : matrix.os == 'ubuntu-latest'
192+ uses : codecov/codecov-action@v4
193+ with :
194+ fail_ci_if_error : false
195+
196+ # === BUILD PACKAGE ===
197+ build :
198+ name : Build Package
199+ runs-on : ubuntu-latest
200+ needs : [lint, test]
201+ if : always() && needs.lint.result == 'success' && needs.test.result == 'success'
202+ steps :
203+ - uses : actions/checkout@v4
204+
205+ - name : Setup .NET
206+ uses : actions/setup-dotnet@v4
207+ with :
208+ dotnet-version : ' 8.0.x'
209+
210+ - name : Restore dependencies
211+ run : dotnet restore
35212
36- # Step 6: Pack the NuGet package
37- - name : Pack
38- run : dotnet pack --configuration Release --no-build --output ./output
213+ - name : Build Release
214+ run : dotnet build --no-restore --configuration Release
215+
216+ - name : Pack NuGet package
217+ run : dotnet pack --no-build --configuration Release --output ./artifacts
218+
219+ - name : Upload artifacts
220+ uses : actions/upload-artifact@v4
221+ with :
222+ name : nuget-package
223+ path : csharp/artifacts/*.nupkg
224+
225+ # === AUTOMATIC RELEASE ===
226+ release :
227+ name : Release
228+ needs : [lint, test, build]
229+ if : always() && github.ref == 'refs/heads/main' && github.event_name == 'push' && needs.lint.result == 'success' && needs.test.result == 'success' && needs.build.result == 'success'
230+ runs-on : ubuntu-latest
231+ permissions :
232+ contents : write
233+ packages : write
234+ steps :
235+ - uses : actions/checkout@v4
236+ with :
237+ fetch-depth : 0
238+
239+ - name : Setup .NET
240+ uses : actions/setup-dotnet@v4
241+ with :
242+ dotnet-version : ' 8.0.x'
243+
244+ - name : Setup Node.js
245+ uses : actions/setup-node@v4
246+ with :
247+ node-version : ' 20.x'
248+
249+ - name : Check for changesets
250+ id : check_changesets
251+ working-directory : .
252+ run : |
253+ # Count changeset files (excluding README.md and config.json)
254+ CHANGESET_COUNT=$(find csharp/.changeset -name "*.md" ! -name "README.md" 2>/dev/null | wc -l)
255+ echo "Found $CHANGESET_COUNT changeset file(s)"
256+ echo "has_changesets=$([[ $CHANGESET_COUNT -gt 0 ]] && echo 'true' || echo 'false')" >> $GITHUB_OUTPUT
257+ echo "changeset_count=$CHANGESET_COUNT" >> $GITHUB_OUTPUT
258+
259+ - name : Merge multiple changesets
260+ if : steps.check_changesets.outputs.has_changesets == 'true' && steps.check_changesets.outputs.changeset_count > 1
261+ working-directory : .
262+ run : |
263+ echo "Multiple changesets detected, merging..."
264+ node scripts/merge-changesets.mjs --dir csharp/.changeset
265+
266+ - name : Version and commit
267+ if : steps.check_changesets.outputs.has_changesets == 'true'
268+ id : version
269+ working-directory : .
270+ run : node scripts/version-and-commit-csharp.mjs --mode changeset
271+
272+ - name : Download artifacts
273+ if : steps.version.outputs.version_committed == 'true'
274+ uses : actions/download-artifact@v4
275+ with :
276+ name : nuget-package
277+ path : ./artifacts
39278
40- # Step 7: Publish the NuGet package
41279 - name : Publish to NuGet
280+ if : steps.version.outputs.version_committed == 'true'
42281 env :
43282 NUGET_API_KEY : ${{ secrets.NUGET_API_KEY }}
44283 run : |
45- dotnet nuget push ./output/*.nupkg \
46- --api-key $NUGET_API_KEY \
47- --source https://api.nuget.org/v3/index.json \
48- --skip-duplicate
284+ if [ -n "$NUGET_API_KEY" ]; then
285+ dotnet nuget push ../artifacts/*.nupkg --api-key $NUGET_API_KEY --source https://api.nuget.org/v3/index.json --skip-duplicate
286+ else
287+ echo "NUGET_API_KEY not set, skipping NuGet publish"
288+ fi
289+
290+ - name : Create GitHub Release
291+ if : steps.version.outputs.version_committed == 'true'
292+ env :
293+ GH_TOKEN : ${{ secrets.GITHUB_TOKEN }}
294+ working-directory : .
295+ run : |
296+ node scripts/create-github-release.mjs \
297+ --release-version "${{ steps.version.outputs.new_version }}" \
298+ --repository "${{ github.repository }}" \
299+ --tag-prefix "csharp-v"
300+
301+ # === MANUAL INSTANT RELEASE ===
302+ instant-release :
303+ name : Instant Release
304+ needs : [lint, test, build]
305+ if : github.event_name == 'workflow_dispatch' && github.event.inputs.release_mode == 'instant' && needs.lint.result == 'success' && needs.test.result == 'success' && needs.build.result == 'success'
306+ runs-on : ubuntu-latest
307+ permissions :
308+ contents : write
309+ packages : write
310+ steps :
311+ - uses : actions/checkout@v4
312+ with :
313+ fetch-depth : 0
314+ token : ${{ secrets.GITHUB_TOKEN }}
315+
316+ - name : Setup .NET
317+ uses : actions/setup-dotnet@v4
318+ with :
319+ dotnet-version : ' 8.0.x'
320+
321+ - name : Setup Node.js
322+ uses : actions/setup-node@v4
323+ with :
324+ node-version : ' 20.x'
325+
326+ - name : Version and commit
327+ id : version
328+ working-directory : .
329+ run : |
330+ node scripts/version-and-commit-csharp.mjs \
331+ --mode instant \
332+ --bump-type "${{ github.event.inputs.bump_type }}" \
333+ --description "${{ github.event.inputs.description }}"
334+
335+ - name : Build package
336+ if : steps.version.outputs.version_committed == 'true'
337+ run : |
338+ dotnet restore
339+ dotnet build --configuration Release
340+ dotnet pack --no-build --configuration Release --output ./artifacts
341+
342+ - name : Publish to NuGet
343+ if : steps.version.outputs.version_committed == 'true'
344+ env :
345+ NUGET_API_KEY : ${{ secrets.NUGET_API_KEY }}
346+ run : |
347+ if [ -n "$NUGET_API_KEY" ]; then
348+ dotnet nuget push ./artifacts/*.nupkg --api-key $NUGET_API_KEY --source https://api.nuget.org/v3/index.json --skip-duplicate
349+ else
350+ echo "NUGET_API_KEY not set, skipping NuGet publish"
351+ fi
352+
353+ - name : Create GitHub Release
354+ if : steps.version.outputs.version_committed == 'true'
355+ env :
356+ GH_TOKEN : ${{ secrets.GITHUB_TOKEN }}
357+ working-directory : .
358+ run : |
359+ node scripts/create-github-release.mjs \
360+ --release-version "${{ steps.version.outputs.new_version }}" \
361+ --repository "${{ github.repository }}" \
362+ --tag-prefix "csharp-v"
0 commit comments