Skip to content

Commit b6598c0

Browse files
authored
ci: build, publish and contributor docs (#6)
* Add CI, releases, and contributor docs * Keep CI documentation focused
1 parent 1de9b62 commit b6598c0

3 files changed

Lines changed: 319 additions & 0 deletions

File tree

.github/workflows/ci.yml

Lines changed: 163 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,163 @@
1+
name: CI
2+
3+
on:
4+
push:
5+
branches:
6+
- main
7+
pull_request:
8+
workflow_dispatch:
9+
10+
permissions:
11+
contents: read
12+
13+
concurrency:
14+
group: ci-${{ github.workflow }}-${{ github.ref }}
15+
cancel-in-progress: true
16+
17+
env:
18+
DOTNET_CLI_TELEMETRY_OPTOUT: "1"
19+
DOTNET_NOLOGO: "1"
20+
D2_VERSION: 0.7.1
21+
22+
jobs:
23+
build-and-test:
24+
name: Build and test (${{ matrix.os }})
25+
runs-on: ${{ matrix.os }}
26+
timeout-minutes: 15
27+
strategy:
28+
fail-fast: false
29+
matrix:
30+
os:
31+
- ubuntu-latest
32+
- windows-latest
33+
34+
steps:
35+
- name: Check out repository
36+
uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6.0.2
37+
with:
38+
persist-credentials: false
39+
40+
- name: Set up .NET
41+
uses: actions/setup-dotnet@26b0ec14cb23fa6904739307f278c14f94c95bf1 # v5.4.0
42+
with:
43+
dotnet-version: 10.0.x
44+
45+
- name: Restore
46+
run: dotnet restore d2lang-cs.sln
47+
48+
- name: Build
49+
run: dotnet build d2lang-cs.sln --configuration Release --no-restore -p:ContinuousIntegrationBuild=true
50+
51+
- name: Install pinned D2 CLI
52+
if: runner.os == 'Linux'
53+
shell: bash
54+
run: |
55+
archive="$RUNNER_TEMP/d2-v${D2_VERSION}-linux-amd64.tar.gz"
56+
install_dir="$RUNNER_TEMP/d2-cli"
57+
curl --proto '=https' --tlsv1.2 --location --fail --silent --show-error \
58+
"https://github.com/d2lang/d2/releases/download/v${D2_VERSION}/d2-v${D2_VERSION}-linux-amd64.tar.gz" \
59+
--output "$archive"
60+
echo "eb172adf59f38d1e5a70ab177591356754ffaf9bebb84e0ca8b767dfb421dad7 $archive" | sha256sum --check --strict
61+
mkdir -p "$install_dir"
62+
tar -xzf "$archive" --strip-components=1 -C "$install_dir"
63+
echo "$install_dir/bin" >> "$GITHUB_PATH"
64+
65+
- name: Test with coverage
66+
run: dotnet test test/Tests.csproj --configuration Release --no-build --logger "trx;LogFileName=tests.trx" --collect "XPlat Code Coverage" --results-directory artifacts/test-results/${{ runner.os }}
67+
68+
- name: Add coverage summary
69+
if: always()
70+
shell: pwsh
71+
run: |
72+
$coveragePath = Get-ChildItem "artifacts/test-results/${{ runner.os }}" -Filter coverage.cobertura.xml -Recurse -ErrorAction SilentlyContinue | Select-Object -First 1
73+
if ($null -eq $coveragePath) {
74+
"## Coverage (${{ runner.os }})`n`nCoverage report was not produced." | Out-File -FilePath $env:GITHUB_STEP_SUMMARY -Append
75+
exit 0
76+
}
77+
78+
[xml] $coverage = Get-Content $coveragePath.FullName
79+
$lineRate = [double]::Parse($coverage.coverage.'line-rate', [Globalization.CultureInfo]::InvariantCulture)
80+
$branchRate = [double]::Parse($coverage.coverage.'branch-rate', [Globalization.CultureInfo]::InvariantCulture)
81+
$linePercent = [Math]::Round($lineRate * 100, 2)
82+
$branchPercent = [Math]::Round($branchRate * 100, 2)
83+
$summary = "## Coverage (${{ runner.os }})`n`n| Metric | Coverage |`n| --- | ---: |`n| Lines | $linePercent% |`n| Branches | $branchPercent% |"
84+
$summary | Out-File -FilePath "artifacts/test-results/${{ runner.os }}/coverage-summary.md"
85+
$summary | Out-File -FilePath $env:GITHUB_STEP_SUMMARY -Append
86+
87+
- name: Upload test results
88+
if: always()
89+
uses: actions/upload-artifact@043fb46d1a93c77aae656e7c1c64a875d1fc6a0a # v7.0.1
90+
with:
91+
name: test-results-${{ runner.os }}
92+
path: artifacts/test-results/${{ runner.os }}/**/*.trx
93+
if-no-files-found: warn
94+
retention-days: 14
95+
96+
- name: Upload coverage report
97+
if: always()
98+
uses: actions/upload-artifact@043fb46d1a93c77aae656e7c1c64a875d1fc6a0a # v7.0.1
99+
with:
100+
name: coverage-${{ runner.os }}
101+
path: |
102+
artifacts/test-results/${{ runner.os }}/**/coverage.cobertura.xml
103+
artifacts/test-results/${{ runner.os }}/coverage-summary.md
104+
if-no-files-found: warn
105+
retention-days: 14
106+
107+
- name: Validate generated D2 syntax
108+
if: runner.os == 'Linux'
109+
shell: bash
110+
run: |
111+
d2 version
112+
dotnet run --project example/cli/d2-sample-cli.csproj --configuration Release --no-build > artifacts/example.d2
113+
d2 validate artifacts/example.d2
114+
115+
package:
116+
name: Validate NuGet package
117+
needs: build-and-test
118+
runs-on: ubuntu-latest
119+
timeout-minutes: 15
120+
121+
steps:
122+
- name: Check out repository
123+
uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6.0.2
124+
with:
125+
persist-credentials: false
126+
127+
- name: Set up .NET
128+
uses: actions/setup-dotnet@26b0ec14cb23fa6904739307f278c14f94c95bf1 # v5.4.0
129+
with:
130+
dotnet-version: 10.0.x
131+
132+
- name: Restore
133+
run: dotnet restore src/d2lang-cs.csproj
134+
135+
- name: Pack with package validation
136+
run: dotnet pack src/d2lang-cs.csproj --configuration Release --no-restore --output artifacts/packages -p:ContinuousIntegrationBuild=true -p:EnablePackageValidation=true
137+
138+
- name: Inspect package contents
139+
shell: bash
140+
run: |
141+
package_path=$(find artifacts/packages -maxdepth 1 -name '*.nupkg' ! -name '*.snupkg' -print -quit)
142+
test -n "$package_path"
143+
unzip -l "$package_path" | tee artifacts/package-contents.txt
144+
grep -q 'lib/net10.0/d2lang-cs.dll' artifacts/package-contents.txt
145+
grep -q 'README.md' artifacts/package-contents.txt
146+
grep -q 'd2_logo.png' artifacts/package-contents.txt
147+
148+
- name: Install package in a clean consumer project
149+
shell: bash
150+
run: |
151+
dotnet new console --framework net10.0 --output artifacts/package-smoke --no-restore
152+
dotnet add artifacts/package-smoke/package-smoke.csproj package d2lang-cs --source "$PWD/artifacts/packages"
153+
dotnet build artifacts/package-smoke/package-smoke.csproj --configuration Release --no-restore
154+
155+
- name: Upload package
156+
uses: actions/upload-artifact@043fb46d1a93c77aae656e7c1c64a875d1fc6a0a # v7.0.1
157+
with:
158+
name: nuget-package
159+
path: |
160+
artifacts/packages/*.nupkg
161+
artifacts/package-contents.txt
162+
if-no-files-found: error
163+
retention-days: 14

.github/workflows/release.yml

Lines changed: 83 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,83 @@
1+
name: Publish NuGet package
2+
3+
on:
4+
push:
5+
tags:
6+
- "v*"
7+
8+
permissions:
9+
contents: read
10+
11+
concurrency:
12+
group: nuget-publish
13+
cancel-in-progress: false
14+
15+
env:
16+
DOTNET_CLI_TELEMETRY_OPTOUT: "1"
17+
DOTNET_NOLOGO: "1"
18+
19+
jobs:
20+
publish:
21+
name: Build, validate, and publish
22+
runs-on: ubuntu-latest
23+
timeout-minutes: 20
24+
environment: nuget.org
25+
26+
steps:
27+
- name: Check out repository
28+
uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6.0.2
29+
with:
30+
persist-credentials: false
31+
32+
- name: Set up .NET
33+
uses: actions/setup-dotnet@26b0ec14cb23fa6904739307f278c14f94c95bf1 # v5.4.0
34+
with:
35+
dotnet-version: 10.0.x
36+
37+
- name: Resolve and validate package version
38+
id: package
39+
shell: bash
40+
run: |
41+
version="${GITHUB_REF_NAME#v}"
42+
43+
if [[ ! "$version" =~ ^[0-9]+\.[0-9]+\.[0-9]+(-[0-9A-Za-z]+([.-][0-9A-Za-z]+)*)?$ ]]; then
44+
echo "Version '$version' is not a supported semantic version." >&2
45+
exit 1
46+
fi
47+
48+
echo "version=$version" >> "$GITHUB_OUTPUT"
49+
50+
- name: Restore
51+
run: dotnet restore d2lang-cs.sln
52+
53+
- name: Test
54+
run: dotnet test test/Tests.csproj --configuration Release --no-restore -p:ContinuousIntegrationBuild=true
55+
56+
- name: Pack with package validation
57+
run: >-
58+
dotnet pack src/d2lang-cs.csproj
59+
--configuration Release
60+
--no-restore
61+
--output artifacts/packages
62+
-p:PackageVersion=${{ steps.package.outputs.version }}
63+
-p:ContinuousIntegrationBuild=true
64+
-p:EnablePackageValidation=true
65+
-p:IncludeSymbols=true
66+
-p:SymbolPackageFormat=snupkg
67+
68+
- name: Upload release packages
69+
uses: actions/upload-artifact@043fb46d1a93c77aae656e7c1c64a875d1fc6a0a # v7.0.1
70+
with:
71+
name: d2lang-cs-${{ steps.package.outputs.version }}
72+
path: artifacts/packages/*.*nupkg
73+
if-no-files-found: error
74+
retention-days: 30
75+
76+
- name: Publish to NuGet.org
77+
env:
78+
NUGET_API_KEY: ${{ secrets.NUGET_API_KEY }}
79+
run: >-
80+
dotnet nuget push "artifacts/packages/*.nupkg"
81+
--api-key "$NUGET_API_KEY"
82+
--source https://api.nuget.org/v3/index.json
83+
--skip-duplicate

CONTRIBUTING.md

Lines changed: 73 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,73 @@
1+
# Contributing to d2lang-cs
2+
3+
Thanks for helping improve `d2lang-cs`. Focused pull requests with tests are the easiest to review and release safely.
4+
5+
## Development setup
6+
7+
Install the [.NET 10 SDK](https://dotnet.microsoft.com/download/dotnet/10.0). The D2 CLI is optional for normal library development and required when checking generated D2 syntax locally. CI currently uses D2 CLI `v0.7.1`.
8+
9+
Fork and clone the repository, then create a branch from `main`:
10+
11+
```bash
12+
git switch main
13+
git pull --ff-only
14+
git switch -c feature/short-description
15+
```
16+
17+
Restore, build, and test from the repository root:
18+
19+
```bash
20+
dotnet restore d2lang-cs.sln
21+
dotnet build d2lang-cs.sln --configuration Release --no-restore
22+
dotnet test test/Tests.csproj --configuration Release --no-build
23+
```
24+
25+
Create the NuGet package with SDK package validation enabled:
26+
27+
```bash
28+
dotnet pack src/d2lang-cs.csproj \
29+
--configuration Release \
30+
--output artifacts/packages \
31+
-p:EnablePackageValidation=true
32+
```
33+
34+
## Validate generated D2
35+
36+
Install the [D2 CLI](https://d2lang.com/tour/install/), run the example, and validate its output:
37+
38+
```bash
39+
dotnet run --project example/cli/d2-sample-cli.csproj \
40+
--configuration Release > /tmp/d2lang-cs-example.d2
41+
d2 validate /tmp/d2lang-cs-example.d2
42+
```
43+
44+
When changing serialization, add tests for both the exact emitted source and parser acceptance. Include cases with reserved characters, quotes, whitespace, multiline text, URLs, and nested containers where relevant.
45+
46+
## Pull requests
47+
48+
- Keep changes focused on one concern.
49+
- Add or update tests for behavior changes.
50+
- Update the README when public behavior, requirements, or compatibility changes.
51+
- Avoid unrelated formatting or generated-file changes.
52+
- Run the release build, tests, package validation, and relevant D2 validation before requesting review.
53+
- Call out intentional compatibility or output-format changes in the description.
54+
55+
Pull requests run clean builds and tests on Linux and Windows. CI also publishes test results, Cobertura coverage artifacts, a coverage summary, a validated NuGet package artifact, and the example D2 source validation result.
56+
57+
## Release process
58+
59+
Releases are published by `.github/workflows/release.yml`. Maintainers should:
60+
61+
1. Confirm the intended commit is on `main` and CI is green.
62+
2. Confirm the `nuget.org` GitHub environment is protected as desired and contains a `NUGET_API_KEY` secret scoped to the `d2lang-cs` package.
63+
3. Choose an unused semantic version such as `1.2.3` or `1.2.3-rc.1`.
64+
4. Create an annotated `v`-prefixed tag and push it:
65+
66+
```bash
67+
git tag -a v1.2.3 -m "d2lang-cs 1.2.3"
68+
git push origin v1.2.3
69+
```
70+
71+
5. Review the `Publish NuGet package` workflow and its package artifact before confirming the package on NuGet.org.
72+
73+
The workflow derives `PackageVersion` from the tag, repeats tests, enables SDK package validation, creates `.nupkg` and `.snupkg` artifacts, and publishes with `dotnet nuget push`. Publishing uses read-only repository permissions and exposes `NUGET_API_KEY` only to the final push step. Duplicate versions are skipped safely, but NuGet package versions are immutable; use a new version if published contents need to change.

0 commit comments

Comments
 (0)