Skip to content

Commit ce093f9

Browse files
authored
Merge 565d424 into f38a812
2 parents f38a812 + 565d424 commit ce093f9

File tree

20 files changed

+373
-120
lines changed

20 files changed

+373
-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: 320 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,320 @@
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+
tags:
44+
- 'v*'
45+
46+
concurrency:
47+
group: ${{ github.workflow }}-${{ github.ref }}
48+
cancel-in-progress: true
49+
50+
env:
51+
DOTNET_SKIP_FIRST_TIME_EXPERIENCE: true
52+
DOTNET_CLI_TELEMETRY_OPTOUT: true
53+
# The Windows runner image has PostgreSQL pre-installed and sets the PGPASSWORD environment variable to "root".
54+
# This conflicts with the default password "postgres", which is used by ikalnytskyi/action-setup-postgres.
55+
# Because action-setup-postgres forgets to update the environment variable accordingly, we do so here.
56+
PGPASSWORD: "postgres"
57+
58+
jobs:
59+
build-and-test:
60+
timeout-minutes: 30
61+
strategy:
62+
fail-fast: false
63+
matrix:
64+
os: [ubuntu-latest, windows-latest, macos-latest]
65+
runs-on: ${{ matrix.os }}
66+
steps:
67+
- name: Setup PostgreSQL
68+
uses: ikalnytskyi/action-setup-postgres@v4
69+
with:
70+
username: postgres
71+
password: postgres
72+
- name: Setup .NET
73+
uses: actions/setup-dotnet@v3
74+
with:
75+
dotnet-version: 6.0.x
76+
- name: Setup PowerShell (Ubuntu)
77+
if: matrix.os == 'ubuntu-latest'
78+
run: |
79+
dotnet tool install --global PowerShell
80+
- name: Setup PowerShell (Windows)
81+
if: matrix.os == 'windows-latest'
82+
shell: cmd
83+
run: |
84+
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
85+
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
86+
- name: Setup PowerShell (macOS)
87+
if: matrix.os == 'macos-latest'
88+
run: |
89+
/bin/bash -c "$(curl -fsSL https://raw.githubusercontent.com/Homebrew/install/HEAD/install.sh)"
90+
brew install --cask powershell
91+
- name: Show installed versions
92+
shell: pwsh
93+
run: |
94+
Write-Host "$(pwsh --version) is installed at $PSHOME"
95+
psql --version
96+
Write-Host "Using .NET SDK: $(dotnet --version)"
97+
#Write-Host "Environment variables:"
98+
#dir env: | %{"{0}={1}" -f $_.Name,$_.Value}
99+
- name: Git checkout
100+
uses: actions/checkout@v3
101+
- name: Restore tools
102+
run: |
103+
dotnet tool restore
104+
- name: Restore packages
105+
run: |
106+
dotnet restore
107+
- name: Calculate version suffix
108+
shell: pwsh
109+
run: |
110+
if ($env:GITHUB_REF_TYPE -eq 'tag') {
111+
# Get the version prefix/suffix from the git tag. Example: 'v1.0.0-preview1-final' => '1.0.0' and 'preview1-final'
112+
$segments = $env:GITHUB_REF_NAME -split "-"
113+
$versionPrefix = $segments[0].TrimStart('v')
114+
$versionSuffix = $segments[1..-1] -join "-"
115+
116+
[xml]$xml = Get-Content Directory.Build.props
117+
$configuredVersionPrefix = $xml.Project.PropertyGroup[0].JsonApiDotNetCoreVersionPrefix
118+
if ($configuredVersionPrefix -ne $versionPrefix) {
119+
Write-Error "Version prefix from git release tag '$versionPrefix' does not match version prefix '$configuredVersionPrefix' stored in Directory.Build.props."
120+
# To recover from this:
121+
# - Delete the GitHub release
122+
# - Run: git push --delete the-invalid-tag
123+
# - Adjust JsonApiDotNetCoreVersionPrefix in Directory.Build.props, commit and push
124+
# - Recreate the GitHub release
125+
}
126+
}
127+
else {
128+
# Get the version suffix from the auto-incrementing build number. Example: '123' => 'master-00123'
129+
$revision = "{0:D5}" -f [convert]::ToInt32($env:GITHUB_RUN_NUMBER, 10)
130+
$versionSuffix = "$($env:GITHUB_HEAD_REF ?? $env:GITHUB_REF_NAME)-$revision"
131+
}
132+
Write-Output "Using version suffix: $versionSuffix"
133+
Write-Output "PACKAGE_VERSION_SUFFIX=$versionSuffix" | Out-File -FilePath $env:GITHUB_ENV -Encoding utf8 -Append
134+
- name: Build
135+
shell: pwsh
136+
run: |
137+
dotnet build --no-restore --configuration Release --version-suffix=$env:PACKAGE_VERSION_SUFFIX
138+
- name: Test
139+
run: |
140+
dotnet test --no-build --configuration Release --collect:"XPlat Code Coverage" --logger "GitHubActions;summary.includeSkippedTests=true" -- RunConfiguration.CollectSourceInformation=true DataCollectionRunSettings.DataCollectors.DataCollector.Configuration.DeterministicReport=true
141+
- name: Upload coverage to codecov.io
142+
if: matrix.os == 'ubuntu-latest'
143+
uses: codecov/codecov-action@v3
144+
- name: Generate packages
145+
shell: pwsh
146+
run: |
147+
dotnet pack --no-build --configuration Release --output $env:GITHUB_WORKSPACE/artifacts/packages --version-suffix=$env:PACKAGE_VERSION_SUFFIX
148+
- name: Upload packages to artifacts
149+
if: matrix.os == 'ubuntu-latest'
150+
uses: actions/upload-artifact@v3
151+
with:
152+
name: packages
153+
path: artifacts/packages
154+
# TODO: Try CodeQL Analysis
155+
# TODO: Add dependabot.yml (GH Security: Enable Dependabot version updates)
156+
# Keep actions up-to-date: https://docs.github.com/en/code-security/dependabot/dependabot-version-updates
157+
# in .github/dependabot.yml:
158+
# version: 2
159+
# updates:
160+
# - package-ecosystem: "github-actions"
161+
# directory: "/"
162+
# schedule:
163+
# interval: "daily"
164+
# labels:
165+
# - "CI/CD"
166+
# commit-message:
167+
# prefix: ci
168+
- name: Generate documentation
169+
shell: pwsh
170+
env:
171+
# TODO: Remove test
172+
#DOCFX_SOURCE_BRANCH_NAME: ${{ github.base_ref || github.ref_name }}
173+
DOCFX_SOURCE_BRANCH_NAME: master
174+
run: |
175+
Write-Host "Using docfx branch name: $env:DOCFX_SOURCE_BRANCH_NAME"
176+
cd docs
177+
& ./generate-examples.ps1
178+
dotnet docfx docfx.json
179+
if ($LastExitCode -ne 0) {
180+
Write-Error "docfx failed with exit code $LastExitCode."
181+
}
182+
Copy-Item CNAME _site/CNAME
183+
Copy-Item home/*.html _site/
184+
Copy-Item home/*.ico _site/
185+
New-Item -Force _site/styles -ItemType Directory | Out-Null
186+
Copy-Item -Recurse home/assets/* _site/styles/
187+
- name: Upload documentation to artifacts
188+
if: matrix.os == 'ubuntu-latest'
189+
uses: actions/upload-artifact@v3
190+
with:
191+
name: documentation
192+
path: docs/_site
193+
194+
inspect-code:
195+
timeout-minutes: 30
196+
runs-on: ubuntu-latest
197+
steps:
198+
- name: Git checkout
199+
uses: actions/checkout@v3
200+
- name: Setup .NET
201+
uses: actions/setup-dotnet@v3
202+
with:
203+
dotnet-version: 6.0.x
204+
- name: Restore tools
205+
run: |
206+
dotnet tool restore
207+
- name: InspectCode
208+
shell: pwsh
209+
run: |
210+
$inspectCodeOutputPath = Join-Path $env:RUNNER_TEMP 'jetbrains-inspectcode-results.xml'
211+
Write-Output "INSPECT_CODE_OUTPUT_PATH=$inspectCodeOutputPath" | Out-File -FilePath $env:GITHUB_ENV -Encoding utf8 -Append
212+
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
213+
- name: Verify outcome
214+
shell: pwsh
215+
run: |
216+
[xml]$xml = Get-Content $env:INSPECT_CODE_OUTPUT_PATH
217+
if ($xml.report.Issues -and $xml.report.Issues.Project) {
218+
foreach ($project in $xml.report.Issues.Project) {
219+
if ($project.Issue.Count -gt 0) {
220+
$project.ForEach({
221+
Write-Output "`nProject $($project.Name)"
222+
$failed = $true
223+
224+
$_.Issue.ForEach({
225+
$issueType = $xml.report.IssueTypes.SelectSingleNode("IssueType[@Id='$($_.TypeId)']")
226+
$severity = $_.Severity ?? $issueType.Severity
227+
228+
Write-Output "[$severity] $($_.File):$($_.Line) $($_.TypeId): $($_.Message)"
229+
})
230+
})
231+
}
232+
}
233+
234+
if ($failed) {
235+
Write-Error "One or more projects failed code inspection."
236+
}
237+
else {
238+
Write-Output "No issues found."
239+
}
240+
}
241+
242+
cleanup-code:
243+
timeout-minutes: 30
244+
runs-on: ubuntu-latest
245+
steps:
246+
- name: Git checkout
247+
uses: actions/checkout@v3
248+
with:
249+
fetch-depth: 2
250+
- name: Setup .NET
251+
uses: actions/setup-dotnet@v3
252+
with:
253+
dotnet-version: 6.0.x
254+
- name: Restore tools
255+
run: |
256+
dotnet tool restore
257+
- name: Restore packages
258+
run: |
259+
dotnet restore
260+
- name: CleanupCode (on PR diff)
261+
# TODO: Test with PR from community user
262+
if: github.event_name == 'pull_request'
263+
shell: pwsh
264+
run: |
265+
# 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.
266+
# The below works because HEAD is detached (at the merge commit), so HEAD~1 is at the base branch.
267+
$headCommitHash = git rev-parse HEAD
268+
$baseCommitHash = git rev-parse HEAD~1
269+
270+
# TODO: What happens when PR is empty?
271+
if ($baseCommitHash -ne $headCommitHash) {
272+
Write-Output "Running code cleanup on commit range $baseCommitHash..$headCommitHash in pull request."
273+
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
274+
}
275+
else {
276+
Write-Output "No changed files in pull request."
277+
}
278+
- name: CleanupCode (on branch)
279+
if: github.event_name == 'push'
280+
shell: pwsh
281+
run: |
282+
Write-Output "Running code cleanup on all files in branch."
283+
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
284+
285+
publish:
286+
timeout-minutes: 30
287+
runs-on: ubuntu-latest
288+
needs: [ build-and-test, inspect-code, cleanup-code ]
289+
# TODO: Test this job does not run from PRs originating from forks
290+
if: github.repository_owner == 'json-api-dotnet'
291+
permissions:
292+
packages: write
293+
contents: write
294+
steps:
295+
- name: Download artifacts
296+
uses: actions/download-artifact@v3
297+
- name: Publish to GitHub Packages
298+
env:
299+
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
300+
shell: pwsh
301+
run: |
302+
# TODO: Update README.md on how to consume packages; make public in GitHub settings at organization level
303+
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'
304+
dotnet nuget push "$env:GITHUB_WORKSPACE/packages/*.nupkg" --api-key "$env:GITHUB_TOKEN" --source 'github'
305+
- name: Publish documentation
306+
#if: github.event_name == 'push' && github.ref == 'refs/heads/master'
307+
uses: peaceiris/actions-gh-pages@v3
308+
with:
309+
# TODO: Test a real deployment (change target to "gh-pages" branch below)
310+
github_token: ${{ secrets.GITHUB_TOKEN }}
311+
publish_branch: gh-pages-test
312+
publish_dir: ./documentation
313+
commit_message: 'Auto-generated documentation from: ${{ github.event.head_commit.message }}'
314+
- name: Publish to NuGet
315+
if: github.event_name == 'push' && startsWith(github.ref, 'refs/tags/v')
316+
env:
317+
NUGET_ORG_API_KEY: ${{ secrets.NUGET_ORG_API_KEY }}
318+
shell: pwsh
319+
run: |
320+
dotnet nuget push "$env:GITHUB_WORKSPACE/packages/*.nupkg" --api-key "$env:NUGET_ORG_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)