Skip to content

Commit 4030455

Browse files
authored
Merge 26b60df into f38a812
2 parents f38a812 + 26b60df commit 4030455

File tree

20 files changed

+360
-120
lines changed

20 files changed

+360
-120
lines changed

.config/dotnet-tools.json

Lines changed: 0 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -14,12 +14,6 @@
1414
"regitlint"
1515
]
1616
},
17-
"codecov.tool": {
18-
"version": "1.13.0",
19-
"commands": [
20-
"codecov"
21-
]
22-
},
2317
"dotnet-reportgenerator-globaltool": {
2418
"version": "5.1.20",
2519
"commands": [

.github/workflows/dotnet.yml

Lines changed: 307 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,307 @@
1+
# This workflow will build a .NET project
2+
# For more information see: https://docs.github.com/en/actions/automating-builds-and-tests/building-and-testing-net
3+
4+
# General links
5+
# https://docs.github.com/en/actions/learn-github-actions/variables#default-environment-variables
6+
# https://docs.github.com/en/actions/learn-github-actions/contexts#github-context
7+
# https://docs.github.com/en/webhooks-and-events/webhooks/webhook-events-and-payloads
8+
# https://docs.github.com/en/actions/learn-github-actions/expressions
9+
10+
# TODO: Post-cleanup
11+
# - Restore notification for failed actions in GitHub settings
12+
# - Update required status checks in GitHub settings
13+
# - Delete GitHub PAT after decommission of AppVeyor
14+
# - Recycle NuGet key? Affects MongoDB
15+
# - Document advantages in PR description
16+
# - Max parallel builds (jobs) is 20 (MacOS 5)
17+
# - Job timeout is 6 hours, workflow 35 days
18+
# - Artifact retention is 90 days
19+
# - Linux runs much faster
20+
# - Managemnt UI integrated in GitHub, includes test summaries
21+
# - Rich ecosystem of actions
22+
# - Shows timings per operation (build, test, cleanup)
23+
# - Improved security
24+
# - Easier to try .NET preview versions
25+
# - Weekly refreshed runner images
26+
# - Subsequent steps execute after timeout, enabling to capture logs on hang
27+
# Disadvantages:
28+
# - There's no way to suppress notification for cancelled jobs
29+
30+
# TODO: Review https://docs.github.com/en/actions/learn-github-actions/understanding-github-actions
31+
# TODO: Review https://docs.github.com/en/actions/security-guides/security-hardening-for-github-actions and https://securitylab.github.com/research/github-actions-preventing-pwn-requests/
32+
# TODO: Adapt name and .yml file name (resets package counter)
33+
# TODO: Update status badge in /README.md (depends on workflow name)
34+
name: .NET
35+
36+
# TODO: Auto-close issues waiting for response: https://github.com/lee-dohm/no-response
37+
38+
on:
39+
push:
40+
branches: [ 'master', 'release/**' ]
41+
pull_request:
42+
branches: [ 'master', 'release/**' ]
43+
44+
concurrency:
45+
group: ${{ github.workflow }}-${{ github.ref }}
46+
cancel-in-progress: true
47+
48+
env:
49+
DOTNET_SKIP_FIRST_TIME_EXPERIENCE: true
50+
DOTNET_CLI_TELEMETRY_OPTOUT: true
51+
# The Windows runner image has PostgreSQL pre-installed and sets the PGPASSWORD environment variable to "root".
52+
# This conflicts with the default password "postgres", which is used by ikalnytskyi/action-setup-postgres.
53+
# Because action-setup-postgres forgets to update the environment variable accordingly, we do so here.
54+
PGPASSWORD: "postgres"
55+
56+
jobs:
57+
build-and-test:
58+
timeout-minutes: 30
59+
strategy:
60+
fail-fast: false
61+
matrix:
62+
os: [ubuntu-latest, windows-latest, macos-latest]
63+
runs-on: ${{ matrix.os }}
64+
steps:
65+
- name: Setup PostgreSQL
66+
uses: ikalnytskyi/action-setup-postgres@v4
67+
with:
68+
username: postgres
69+
password: postgres
70+
- name: Setup .NET
71+
uses: actions/setup-dotnet@v3
72+
with:
73+
dotnet-version: 6.0.x
74+
- name: Setup PowerShell (Ubuntu)
75+
if: matrix.os == 'ubuntu-latest'
76+
run: |
77+
dotnet tool install --global PowerShell
78+
- name: Setup PowerShell (Windows)
79+
if: matrix.os == 'windows-latest'
80+
shell: cmd
81+
run: |
82+
curl --location --output "%RUNNER_TEMP%\PowerShell-7.3.6-win-x64.msi" https://github.com/PowerShell/PowerShell/releases/download/v7.3.6/PowerShell-7.3.6-win-x64.msi
83+
msiexec.exe /package "%RUNNER_TEMP%\PowerShell-7.3.6-win-x64.msi" /quiet USE_MU=1 ENABLE_MU=1 ADD_PATH=1 DISABLE_TELEMETRY=1
84+
- name: Setup PowerShell (macOS)
85+
if: matrix.os == 'macos-latest'
86+
run: |
87+
/bin/bash -c "$(curl -fsSL https://raw.githubusercontent.com/Homebrew/install/HEAD/install.sh)"
88+
brew install --cask powershell
89+
- name: Show installed versions
90+
shell: pwsh
91+
run: |
92+
Write-Host "$(pwsh --version) is installed at $PSHOME"
93+
psql --version
94+
Write-Host "Using .NET SDK: $(dotnet --version)"
95+
#Write-Host "Environment variables:"
96+
#dir env: | %{"{0}={1}" -f $_.Name,$_.Value}
97+
- name: Git checkout
98+
uses: actions/checkout@v3
99+
- name: Restore tools
100+
run: |
101+
dotnet tool restore
102+
- name: Restore packages
103+
run: |
104+
dotnet restore
105+
- name: Calculate version suffix
106+
shell: pwsh
107+
run: |
108+
# TODO: Fail when package tag prefix does not match the version prefix stored in Directory.Build.props?
109+
if ($env:GITHUB_REF_TYPE -eq 'tag') {
110+
# Get the version suffix from the repo tag. Example: v1.0.0-preview1-final => preview1-final
111+
$segments = $env:GITHUB_REF_NAME -split "-"
112+
$suffixSegments = $segments[1..-1]
113+
$versionSuffix = $suffixSegments -join "-"
114+
}
115+
else {
116+
# Get the version suffix from the auto-incrementing build number. Example: 123 => master-0123
117+
$revision = "{0:D4}" -f [convert]::ToInt32($env:GITHUB_RUN_NUMBER, 10)
118+
$versionSuffix = "$($env:GITHUB_HEAD_REF ?? $env:GITHUB_REF_NAME)-$revision"
119+
}
120+
Write-Output "Using version suffix: $versionSuffix"
121+
Write-Output "PACKAGE_VERSION_SUFFIX=$versionSuffix" | Out-File -FilePath $env:GITHUB_ENV -Encoding utf8 -Append
122+
- name: Build
123+
shell: pwsh
124+
run: |
125+
dotnet build --no-restore --configuration Release --version-suffix=$env:PACKAGE_VERSION_SUFFIX
126+
- name: Test
127+
run: |
128+
dotnet test --no-build --configuration Release --collect:"XPlat Code Coverage" --logger "GitHubActions;summary.includeSkippedTests=true" -- RunConfiguration.CollectSourceInformation=true DataCollectionRunSettings.DataCollectors.DataCollector.Configuration.DeterministicReport=true
129+
- name: Upload coverage to codecov.io
130+
if: matrix.os == 'ubuntu-latest'
131+
uses: codecov/codecov-action@v3
132+
- name: Generate packages
133+
shell: pwsh
134+
run: |
135+
dotnet pack --no-build --configuration Release --output $env:GITHUB_WORKSPACE/artifacts/packages --version-suffix=$env:PACKAGE_VERSION_SUFFIX
136+
- name: Upload packages to artifacts
137+
if: matrix.os == 'ubuntu-latest'
138+
uses: actions/upload-artifact@v3
139+
with:
140+
name: packages
141+
path: artifacts/packages
142+
# TODO: Try CodeQL Analysis
143+
# TODO: Add dependabot.yml (GH Security: Enable Dependabot version updates)
144+
# Keep actions up-to-date: https://docs.github.com/en/code-security/dependabot/dependabot-version-updates
145+
# in .github/dependabot.yml:
146+
# version: 2
147+
# updates:
148+
# - package-ecosystem: "github-actions"
149+
# directory: "/"
150+
# schedule:
151+
# interval: "daily"
152+
# labels:
153+
# - "CI/CD"
154+
# commit-message:
155+
# prefix: ci
156+
- name: Generate documentation
157+
shell: pwsh
158+
env:
159+
DOCFX_SOURCE_BRANCH_NAME: ${{ github.base_ref || github.ref_name }}
160+
run: |
161+
Write-Host "Using docfx branch name: $env:DOCFX_SOURCE_BRANCH_NAME"
162+
cd docs
163+
& ./generate-examples.ps1
164+
dotnet docfx docfx.json
165+
if ($LastExitCode -ne 0) {
166+
Write-Error "docfx failed with exit code $LastExitCode."
167+
}
168+
Copy-Item CNAME _site/CNAME
169+
Copy-Item home/*.html _site/
170+
Copy-Item home/*.ico _site/
171+
New-Item -Force _site/styles -ItemType Directory | Out-Null
172+
Copy-Item -Recurse home/assets/* _site/styles/
173+
- name: Upload documentation to artifacts
174+
if: matrix.os == 'ubuntu-latest'
175+
uses: actions/upload-artifact@v3
176+
with:
177+
name: documentation
178+
path: docs/_site
179+
180+
inspect-code:
181+
timeout-minutes: 30
182+
runs-on: ubuntu-latest
183+
steps:
184+
- name: Git checkout
185+
uses: actions/checkout@v3
186+
- name: Setup .NET
187+
uses: actions/setup-dotnet@v3
188+
with:
189+
dotnet-version: 6.0.x
190+
- name: Restore tools
191+
run: |
192+
dotnet tool restore
193+
- name: InspectCode
194+
shell: pwsh
195+
run: |
196+
$inspectCodeOutputPath = Join-Path $env:RUNNER_TEMP 'jetbrains-inspectcode-results.xml'
197+
Write-Output "INSPECT_CODE_OUTPUT_PATH=$inspectCodeOutputPath" | Out-File -FilePath $env:GITHUB_ENV -Encoding utf8 -Append
198+
dotnet jb inspectcode JsonApiDotNetCore.sln --build --output="$inspectCodeOutputPath" --profile=WarningSeverities.DotSettings --properties:Configuration=Release --severity=WARNING --verbosity=WARN -dsl=GlobalAll -dsl=GlobalPerProduct -dsl=SolutionPersonal -dsl=ProjectPersonal
199+
- name: Verify outcome
200+
shell: pwsh
201+
run: |
202+
[xml]$xml = Get-Content $env:INSPECT_CODE_OUTPUT_PATH
203+
if ($xml.report.Issues -and $xml.report.Issues.Project) {
204+
foreach ($project in $xml.report.Issues.Project) {
205+
if ($project.Issue.Count -gt 0) {
206+
$project.ForEach({
207+
Write-Output "`nProject $($project.Name)"
208+
$failed = $true
209+
210+
$_.Issue.ForEach({
211+
$issueType = $xml.report.IssueTypes.SelectSingleNode("IssueType[@Id='$($_.TypeId)']")
212+
$severity = $_.Severity ?? $issueType.Severity
213+
214+
Write-Output "[$severity] $($_.File):$($_.Line) $($_.TypeId): $($_.Message)"
215+
})
216+
})
217+
}
218+
}
219+
220+
if ($failed) {
221+
Write-Error "One or more projects failed code inspection."
222+
}
223+
else {
224+
Write-Output "No issues found."
225+
}
226+
}
227+
228+
cleanup-code:
229+
timeout-minutes: 30
230+
runs-on: ubuntu-latest
231+
steps:
232+
- name: Git checkout
233+
uses: actions/checkout@v3
234+
with:
235+
fetch-depth: 2
236+
- name: Setup .NET
237+
uses: actions/setup-dotnet@v3
238+
with:
239+
dotnet-version: 6.0.x
240+
- name: Restore tools
241+
run: |
242+
dotnet tool restore
243+
- name: Restore packages
244+
run: |
245+
dotnet restore
246+
- name: CleanupCode (on PR diff)
247+
# TODO: Test with PR from community user
248+
if: github.event_name == 'pull_request'
249+
shell: pwsh
250+
run: |
251+
# Not using the environment variables for SHAs, because they may be outdated. This happens on force-push after the build is queued, but before it starts.
252+
# The below works because HEAD is detached (at the merge commit), so HEAD~1 is at the base branch.
253+
$headCommitHash = git rev-parse HEAD
254+
$baseCommitHash = git rev-parse HEAD~1
255+
256+
# TODO: What happens when PR is empty?
257+
if ($baseCommitHash -ne $headCommitHash) {
258+
Write-Output "Running code cleanup on commit range $baseCommitHash..$headCommitHash in pull request."
259+
dotnet regitlint -s JsonApiDotNetCore.sln --print-command --skip-tool-check --max-runs=5 --jb-profile="JADNC Full Cleanup" --jb --properties:Configuration=Release --jb --verbosity=WARN -f commits -a $headCommitHash -b $baseCommitHash --fail-on-diff --print-diff
260+
}
261+
else {
262+
Write-Output "No changed files in pull request."
263+
}
264+
- name: CleanupCode (on branch)
265+
if: github.event_name == 'push'
266+
shell: pwsh
267+
run: |
268+
Write-Output "Running code cleanup on all files in branch."
269+
dotnet regitlint -s JsonApiDotNetCore.sln --print-command --skip-tool-check --jb-profile="JADNC Full Cleanup" --jb --properties:Configuration=Release --jb --verbosity=WARN --fail-on-diff --print-diff
270+
271+
publish:
272+
timeout-minutes: 30
273+
runs-on: ubuntu-latest
274+
needs: [ build-and-test, inspect-code, cleanup-code ]
275+
# TODO: Test this job does not run from PRs originating from forks
276+
if: github.repository_owner == 'json-api-dotnet'
277+
permissions:
278+
packages: write
279+
contents: write
280+
steps:
281+
- name: Download artifacts
282+
uses: actions/download-artifact@v3
283+
- name: Publish to GitHub Packages
284+
env:
285+
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
286+
shell: pwsh
287+
run: |
288+
# TODO: Update README.md on how to consume packages; make public in GitHub settings at organization level
289+
dotnet nuget add source --username 'json-api-dotnet' --password "$env:GITHUB_TOKEN" --store-password-in-clear-text --name 'github' 'https://nuget.pkg.github.com/json-api-dotnet/index.json'
290+
dotnet nuget push "$env:GITHUB_WORKSPACE/packages/*.nupkg" --api-key "$env:GITHUB_TOKEN" --source 'github'
291+
- name: Publish documentation
292+
#if: github.event_name == 'push' && github.ref == 'refs/heads/master'
293+
uses: peaceiris/actions-gh-pages@v3
294+
with:
295+
# TODO: Test a real deployment (change target to "gh-pages" branch below)
296+
github_token: ${{ secrets.GITHUB_TOKEN }}
297+
publish_branch: gh-pages-test
298+
publish_dir: ./documentation
299+
commit_message: 'Auto-generated documentation from: ${{ github.event.head_commit.message }}'
300+
- name: Publish to NuGet
301+
# TODO: Test this
302+
if: github.event_name == 'push' && startsWith(github.ref, 'refs/tags/v')
303+
env:
304+
NUGET_API_KEY: ${{ secrets.NUGET_API_KEY }}
305+
shell: pwsh
306+
run: |
307+
dotnet nuget push "$env:GITHUB_WORKSPACE/packages/*.nupkg" --api-key "$env:NUGET_API_KEY" --source 'nuget.org'

Directory.Build.props

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -21,6 +21,10 @@
2121
<AdditionalFiles Include="$(MSBuildThisFileDirectory)CSharpGuidelinesAnalyzer.config" Visible="False" />
2222
</ItemGroup>
2323

24+
<PropertyGroup Condition="'$(APPVEYOR)' != '' Or '$(CI)' != ''">
25+
<ContinuousIntegrationBuild>true</ContinuousIntegrationBuild>
26+
</PropertyGroup>
27+
2428
<PropertyGroup Condition="'$(Configuration)'=='Release'">
2529
<NoWarn>$(NoWarn);1591</NoWarn>
2630
<TreatWarningsAsErrors>true</TreatWarningsAsErrors>
@@ -34,6 +38,7 @@
3438
<!-- Test Project Dependencies -->
3539
<PropertyGroup>
3640
<CoverletVersion>6.0.*</CoverletVersion>
41+
<GitHubActionsTestLoggerVersion>2.3.*</GitHubActionsTestLoggerVersion>
3742
<MoqVersion>4.18.*</MoqVersion>
3843
<TestSdkVersion>17.6.*</TestSdkVersion>
3944
</PropertyGroup>

0 commit comments

Comments
 (0)