Skip to content

Commit 2b5ed91

Browse files
committed
ci: add .NET 6/8 + net46 CI/CD, quality gates, commitlint, release with TFM verification; owners/protection docs
1 parent 6e3376e commit 2b5ed91

File tree

7 files changed

+430
-0
lines changed

7 files changed

+430
-0
lines changed

.github/BRANCH_PROTECTION.md

Lines changed: 19 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,19 @@
1+
# Branch Protection Guidance for AiDotNet
2+
3+
We recommend enabling branch protection on `master` with these required status checks:
4+
5+
- CI (.NET) / Lint and Format Check
6+
- CI (.NET) / Build
7+
- CI (.NET) / Test (.NET 8.0.x)
8+
- CI (.NET) / Integration Tests
9+
- CI (.NET) / All Checks Passed
10+
- Quality Gates (.NET) / Publish Size Analysis
11+
- Commit Message Lint / commitlint
12+
13+
Also enable:
14+
- Require pull request reviews (e.g., 1–2 approvals)
15+
- Dismiss stale approvals on new commits (optional)
16+
- Require status checks to pass before merging
17+
- Require branches to be up to date before merging
18+
19+
Note: The exact names shown in GitHub’s UI may include the workflow/job prefixes. Use the checks as they appear on a PR to configure the protection rules.

.github/CODEOWNERS

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,6 @@
1+
# CODEOWNERS for AiDotNet
2+
# Update these handles to your actual maintainers/teams.
3+
# Lines are of the form: <pattern> <owner1> <owner2>
4+
5+
* @cheat # TODO: replace with your org/team, e.g. @ooples/core or @ooples/ai-team
6+

.github/WORKFLOW_SECRETS.md

Lines changed: 15 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,15 @@
1+
# Workflow Secrets for AiDotNet CI/CD
2+
3+
Set these GitHub repository secrets (Settings → Secrets and variables → Actions → New repository secret):
4+
5+
- `CODECOV_TOKEN`: Codecov upload token
6+
- Used by: `.github/workflows/ci.yml` (coverage upload)
7+
8+
- `NUGET_API_KEY`: NuGet API key for publishing packages
9+
- Used by: `.github/workflows/release.yml` (publish to NuGet)
10+
11+
Important:
12+
- Do NOT commit secrets to the repository.
13+
- These secrets may be shared across multiple repos; after rollout here, update other repos to ensure consistency.
14+
- If rotating keys, update all dependent repos’ secrets simultaneously to avoid broken pipelines.
15+

.github/workflows/ci.yml

Lines changed: 181 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,181 @@
1+
name: CI (.NET)
2+
3+
on:
4+
pull_request:
5+
branches: [ master ]
6+
push:
7+
branches: [ master ]
8+
9+
concurrency:
10+
group: ${{ github.workflow }}-${{ github.ref }}
11+
cancel-in-progress: true
12+
13+
jobs:
14+
lint-and-format:
15+
name: Lint and Format Check
16+
runs-on: ubuntu-latest
17+
timeout-minutes: 10
18+
steps:
19+
- name: Checkout code
20+
uses: actions/checkout@v4
21+
22+
- name: Setup .NET 8.0.x
23+
uses: actions/setup-dotnet@v4
24+
with:
25+
dotnet-version: 8.0.x
26+
27+
- name: Restore
28+
run: dotnet restore AiDotNet.sln
29+
- name: Verify code style (dotnet format)
30+
run: |
31+
dotnet tool update -g dotnet-format || dotnet tool install -g dotnet-format
32+
export PATH="$PATH:$HOME/.dotnet/tools"
33+
dotnet format AiDotNet.sln --verify-no-changes || (echo "Run 'dotnet format' locally to fix style issues." && exit 1)
34+
build:
35+
name: Build
36+
strategy:
37+
matrix:
38+
dotnet: ['6.0.x', '8.0.x']
39+
fail-fast: false
40+
runs-on: ubuntu-latest
41+
timeout-minutes: 15
42+
steps:
43+
- name: Checkout code
44+
uses: actions/checkout@v4
45+
46+
- name: Setup .NET 8.0.x
47+
uses: actions/setup-dotnet@v4
48+
with:
49+
dotnet-version: 8.0.x
50+
51+
- name: Restore
52+
run: dotnet restore AiDotNet.sln
53+
- name: Build
54+
run: dotnet build AiDotNet.sln --configuration Release --no-restore
55+
56+
- name: Publish core library (sanity)
57+
run: |
58+
if [ -f "src/AiDotNet.csproj" ]; then
59+
dotnet publish src/AiDotNet.csproj -c Release -o out
60+
else
61+
echo "AiDotNet.csproj not found, skipping publish sanity"
62+
fi
63+
64+
- name: Upload build artifacts
65+
uses: actions/upload-artifact@v4
66+
with:
67+
name: build-${{ github.sha }}
68+
path: |
69+
out/
70+
**/bin/Release/
71+
if-no-files-found: ignore
72+
retention-days: 7
73+
74+
test:
75+
name: Test (.NET ${{ matrix.dotnet }})
76+
runs-on: ubuntu-latest
77+
timeout-minutes: 20
78+
strategy:
79+
matrix:
80+
dotnet: ['6.0.x', '8.0.x']
81+
fail-fast: false
82+
steps:
83+
- name: Checkout code
84+
uses: actions/checkout@v4
85+
86+
- name: Setup .NET 8.0.x
87+
uses: actions/setup-dotnet@v4
88+
with:
89+
dotnet-version: 8.0.x
90+
91+
- name: Restore
92+
run: dotnet restore AiDotNet.sln
93+
- name: Run tests with coverage
94+
run: |
95+
dotnet test AiDotNet.sln --configuration Release --collect:"XPlat Code Coverage" --results-directory ./TestResults
96+
97+
- name: Upload coverage to Codecov (only uploads if secret set)\n uses: codecov/codecov-action@v4
98+
with:
99+
token: ${{ secrets.CODECOV_TOKEN }}
100+
files: |
101+
./TestResults/**/coverage.cobertura.xml
102+
flags: unittests
103+
fail_ci_if_error: false
104+
105+
integration-test:
106+
name: Integration Tests
107+
runs-on: ubuntu-latest
108+
timeout-minutes: 15
109+
needs: build
110+
steps:
111+
- name: Checkout code
112+
uses: actions/checkout@v4
113+
114+
- name: Setup .NET 8.0.x
115+
uses: actions/setup-dotnet@v4
116+
with:
117+
dotnet-version: 8.0.x
118+
119+
- name: Run integration console (if present)
120+
run: |
121+
if [ -f "testconsole/AiDotNetTestConsole.csproj" ]; then
122+
dotnet run -c Release -p testconsole/AiDotNetTestConsole.csproj || exit 1
123+
else
124+
echo "No integration console project found; skipping"
125+
fi
126+
127+
status-check:
128+
name: All Checks Passed
129+
runs-on: ubuntu-latest
130+
if: always()
131+
needs: [lint-and-format, build, build-netfx, test, integration-test]
132+
steps:
133+
- name: Verify job outcomes
134+
run: |
135+
[ "${{ needs.lint-and-format.result }}" = "success" ] || (echo "Lint/format failed" && exit 1)
136+
[ "${{ needs.build.result }}" = "success" ] || (echo "Build failed" && exit 1)
137+
[ "${{ needs.test.result }}" = "success" ] || (echo "Tests failed" && exit 1)
138+
[ "${{ needs.integration-test.result }}" = "success" ] || (echo "Integration tests failed" && exit 1)
139+
echo "All checks passed!"
140+
141+
142+
143+
144+
145+
146+
147+
build-netfx:
148+
name: Build (.NET Framework 4.6)
149+
runs-on: windows-latest
150+
timeout-minutes: 15
151+
steps:
152+
- name: Checkout code
153+
uses: actions/checkout@v4
154+
155+
- name: Setup .NET SDK
156+
uses: actions/setup-dotnet@v4
157+
with:
158+
dotnet-version: 8.0.x
159+
160+
- name: Restore
161+
shell: pwsh
162+
run: |
163+
dotnet restore
164+
165+
- name: Build (Windows MSBuild)
166+
shell: pwsh
167+
run: |
168+
# Build all projects; if multi-targeting includes net46, MSBuild will build it on Windows
169+
dotnet build -c Release --no-restore
170+
171+
- name: Upload netfx build artifacts
172+
if: always()
173+
uses: actions/upload-artifact@v4
174+
with:
175+
name: netfx-build-${{ github.sha }}
176+
path: |
177+
**/bin/Release/
178+
if-no-files-found: ignore
179+
retention-days: 7
180+
181+

.github/workflows/commitlint.yml

Lines changed: 19 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,19 @@
1+
name: Commit Message Lint
2+
3+
on:
4+
pull_request:
5+
pull_request_target:
6+
7+
jobs:
8+
commitlint:
9+
runs-on: ubuntu-latest
10+
steps:
11+
- name: Check out the PR
12+
uses: actions/checkout@v4
13+
with:
14+
fetch-depth: 0
15+
- name: Lint commits
16+
uses: wagoid/commitlint-github-action@v6
17+
with:
18+
configFile: ''
19+
Lines changed: 78 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,78 @@
1+
name: Quality Gates (.NET)
2+
3+
on:
4+
pull_request:
5+
branches: [ master ]
6+
push:
7+
branches: [ master ]
8+
9+
concurrency:
10+
group: ${{ github.workflow }}-${{ github.ref }}
11+
cancel-in-progress: true
12+
13+
jobs:
14+
artifact-size:
15+
name: Publish Size Analysis
16+
runs-on: ubuntu-latest
17+
timeout-minutes: 15
18+
steps:
19+
- name: Checkout code
20+
uses: actions/checkout@v4
21+
with:
22+
fetch-depth: 0
23+
24+
- name: Setup .NET
25+
uses: actions/setup-dotnet@v4
26+
with:
27+
dotnet-version: 8.0.x
28+
29+
- name: Publish library
30+
run: |
31+
if [ -f "src/AiDotNet.csproj" ]; then
32+
dotnet publish src/AiDotNet.csproj -c Release -o publish
33+
else
34+
# fallback: publish all projects in src
35+
for p in src/*.csproj; do dotnet publish "$p" -c Release -o publish || true; done
36+
fi
37+
38+
- name: Analyze size vs baseline
39+
id: size
40+
run: |
41+
CURRENT_SIZE=$(du -sb publish | cut -f1)
42+
CURRENT_MB=$(echo "scale=2; $CURRENT_SIZE/1024/1024" | bc)
43+
echo "current_size=$CURRENT_SIZE" >> $GITHUB_OUTPUT
44+
echo "current_mb=$CURRENT_MB" >> $GITHUB_OUTPUT
45+
echo "Current publish size: ${CURRENT_MB} MB"
46+
47+
BASELINE_FILE=".github/artifact-size-baseline.txt"
48+
if [ ! -f "$BASELINE_FILE" ]; then
49+
echo "$CURRENT_SIZE" > "$BASELINE_FILE"
50+
echo "baseline_exists=false" >> $GITHUB_OUTPUT
51+
echo "Created baseline (${CURRENT_MB} MB)"
52+
exit 0
53+
fi
54+
55+
BASELINE=$(cat "$BASELINE_FILE")
56+
BASELINE_MB=$(echo "scale=2; $BASELINE/1024/1024" | bc)
57+
CHANGE=$(echo "scale=2; (($CURRENT_SIZE - $BASELINE) * 100) / $BASELINE" | bc)
58+
echo "baseline_exists=true" >> $GITHUB_OUTPUT
59+
echo "baseline_mb=$BASELINE_MB" >> $GITHUB_OUTPUT
60+
echo "percent_change=$CHANGE" >> $GITHUB_OUTPUT
61+
echo "Size change: ${CHANGE}% (baseline ${BASELINE_MB} MB)"
62+
63+
if (( $(echo "$CHANGE > 5" | bc -l) )); then
64+
echo "Error: publish size increased by ${CHANGE}% (> +5%)"
65+
exit 1
66+
fi
67+
68+
- name: Upload size report
69+
if: always()
70+
uses: actions/upload-artifact@v4
71+
with:
72+
name: publish-size-${{ github.sha }}
73+
path: |
74+
publish/
75+
.github/artifact-size-baseline.txt
76+
if-no-files-found: ignore
77+
retention-days: 7
78+

0 commit comments

Comments
 (0)