Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
163 changes: 163 additions & 0 deletions .github/workflows/ci.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,163 @@
name: CI

on:
push:
branches:
- main
pull_request:
workflow_dispatch:

permissions:
contents: read

concurrency:
group: ci-${{ github.workflow }}-${{ github.ref }}
cancel-in-progress: true

env:
DOTNET_CLI_TELEMETRY_OPTOUT: "1"
DOTNET_NOLOGO: "1"
D2_VERSION: 0.7.1

jobs:
build-and-test:
name: Build and test (${{ matrix.os }})
runs-on: ${{ matrix.os }}
timeout-minutes: 15
strategy:
fail-fast: false
matrix:
os:
- ubuntu-latest
- windows-latest

steps:
- name: Check out repository
uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6.0.2
with:
persist-credentials: false

- name: Set up .NET
uses: actions/setup-dotnet@26b0ec14cb23fa6904739307f278c14f94c95bf1 # v5.4.0
with:
dotnet-version: 10.0.x

- name: Restore
run: dotnet restore d2lang-cs.sln

- name: Build
run: dotnet build d2lang-cs.sln --configuration Release --no-restore -p:ContinuousIntegrationBuild=true

- name: Install pinned D2 CLI
if: runner.os == 'Linux'
shell: bash
run: |
archive="$RUNNER_TEMP/d2-v${D2_VERSION}-linux-amd64.tar.gz"
install_dir="$RUNNER_TEMP/d2-cli"
curl --proto '=https' --tlsv1.2 --location --fail --silent --show-error \
"https://github.com/d2lang/d2/releases/download/v${D2_VERSION}/d2-v${D2_VERSION}-linux-amd64.tar.gz" \
--output "$archive"
echo "eb172adf59f38d1e5a70ab177591356754ffaf9bebb84e0ca8b767dfb421dad7 $archive" | sha256sum --check --strict
mkdir -p "$install_dir"
tar -xzf "$archive" --strip-components=1 -C "$install_dir"
echo "$install_dir/bin" >> "$GITHUB_PATH"

- name: Test with coverage
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 }}

- name: Add coverage summary
if: always()
shell: pwsh
run: |
$coveragePath = Get-ChildItem "artifacts/test-results/${{ runner.os }}" -Filter coverage.cobertura.xml -Recurse -ErrorAction SilentlyContinue | Select-Object -First 1
if ($null -eq $coveragePath) {
"## Coverage (${{ runner.os }})`n`nCoverage report was not produced." | Out-File -FilePath $env:GITHUB_STEP_SUMMARY -Append
exit 0
}

[xml] $coverage = Get-Content $coveragePath.FullName
$lineRate = [double]::Parse($coverage.coverage.'line-rate', [Globalization.CultureInfo]::InvariantCulture)
$branchRate = [double]::Parse($coverage.coverage.'branch-rate', [Globalization.CultureInfo]::InvariantCulture)
$linePercent = [Math]::Round($lineRate * 100, 2)
$branchPercent = [Math]::Round($branchRate * 100, 2)
$summary = "## Coverage (${{ runner.os }})`n`n| Metric | Coverage |`n| --- | ---: |`n| Lines | $linePercent% |`n| Branches | $branchPercent% |"
$summary | Out-File -FilePath "artifacts/test-results/${{ runner.os }}/coverage-summary.md"
$summary | Out-File -FilePath $env:GITHUB_STEP_SUMMARY -Append

- name: Upload test results
if: always()
uses: actions/upload-artifact@043fb46d1a93c77aae656e7c1c64a875d1fc6a0a # v7.0.1
with:
name: test-results-${{ runner.os }}
path: artifacts/test-results/${{ runner.os }}/**/*.trx
if-no-files-found: warn
retention-days: 14

- name: Upload coverage report
if: always()
uses: actions/upload-artifact@043fb46d1a93c77aae656e7c1c64a875d1fc6a0a # v7.0.1
with:
name: coverage-${{ runner.os }}
path: |
artifacts/test-results/${{ runner.os }}/**/coverage.cobertura.xml
artifacts/test-results/${{ runner.os }}/coverage-summary.md
if-no-files-found: warn
retention-days: 14

- name: Validate generated D2 syntax
if: runner.os == 'Linux'
shell: bash
run: |
d2 version
dotnet run --project example/cli/d2-sample-cli.csproj --configuration Release --no-build > artifacts/example.d2
d2 validate artifacts/example.d2

package:
name: Validate NuGet package
needs: build-and-test
runs-on: ubuntu-latest
timeout-minutes: 15

steps:
- name: Check out repository
uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6.0.2
with:
persist-credentials: false

- name: Set up .NET
uses: actions/setup-dotnet@26b0ec14cb23fa6904739307f278c14f94c95bf1 # v5.4.0
with:
dotnet-version: 10.0.x

- name: Restore
run: dotnet restore src/d2lang-cs.csproj

- name: Pack with package validation
run: dotnet pack src/d2lang-cs.csproj --configuration Release --no-restore --output artifacts/packages -p:ContinuousIntegrationBuild=true -p:EnablePackageValidation=true

- name: Inspect package contents
shell: bash
run: |
package_path=$(find artifacts/packages -maxdepth 1 -name '*.nupkg' ! -name '*.snupkg' -print -quit)
test -n "$package_path"
unzip -l "$package_path" | tee artifacts/package-contents.txt
grep -q 'lib/net10.0/d2lang-cs.dll' artifacts/package-contents.txt
grep -q 'README.md' artifacts/package-contents.txt
grep -q 'd2_logo.png' artifacts/package-contents.txt

- name: Install package in a clean consumer project
shell: bash
run: |
dotnet new console --framework net10.0 --output artifacts/package-smoke --no-restore
dotnet add artifacts/package-smoke/package-smoke.csproj package d2lang-cs --source "$PWD/artifacts/packages"
dotnet build artifacts/package-smoke/package-smoke.csproj --configuration Release --no-restore

- name: Upload package
uses: actions/upload-artifact@043fb46d1a93c77aae656e7c1c64a875d1fc6a0a # v7.0.1
with:
name: nuget-package
path: |
artifacts/packages/*.nupkg
artifacts/package-contents.txt
if-no-files-found: error
retention-days: 14
83 changes: 83 additions & 0 deletions .github/workflows/release.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,83 @@
name: Publish NuGet package

on:
push:
tags:
- "v*"

permissions:
contents: read

concurrency:
group: nuget-publish
cancel-in-progress: false

env:
DOTNET_CLI_TELEMETRY_OPTOUT: "1"
DOTNET_NOLOGO: "1"

jobs:
publish:
name: Build, validate, and publish
runs-on: ubuntu-latest
timeout-minutes: 20
environment: nuget.org

steps:
- name: Check out repository
uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6.0.2
with:
persist-credentials: false

- name: Set up .NET
uses: actions/setup-dotnet@26b0ec14cb23fa6904739307f278c14f94c95bf1 # v5.4.0
with:
dotnet-version: 10.0.x

- name: Resolve and validate package version
id: package
shell: bash
run: |
version="${GITHUB_REF_NAME#v}"

if [[ ! "$version" =~ ^[0-9]+\.[0-9]+\.[0-9]+(-[0-9A-Za-z]+([.-][0-9A-Za-z]+)*)?$ ]]; then
echo "Version '$version' is not a supported semantic version." >&2
exit 1
fi

echo "version=$version" >> "$GITHUB_OUTPUT"

- name: Restore
run: dotnet restore d2lang-cs.sln

- name: Test
run: dotnet test test/Tests.csproj --configuration Release --no-restore -p:ContinuousIntegrationBuild=true

- name: Pack with package validation
run: >-
dotnet pack src/d2lang-cs.csproj
--configuration Release
--no-restore
--output artifacts/packages
-p:PackageVersion=${{ steps.package.outputs.version }}
-p:ContinuousIntegrationBuild=true
-p:EnablePackageValidation=true
-p:IncludeSymbols=true
-p:SymbolPackageFormat=snupkg

- name: Upload release packages
uses: actions/upload-artifact@043fb46d1a93c77aae656e7c1c64a875d1fc6a0a # v7.0.1
with:
name: d2lang-cs-${{ steps.package.outputs.version }}
path: artifacts/packages/*.*nupkg
if-no-files-found: error
retention-days: 30

- name: Publish to NuGet.org
env:
NUGET_API_KEY: ${{ secrets.NUGET_API_KEY }}
run: >-
dotnet nuget push "artifacts/packages/*.nupkg"
--api-key "$NUGET_API_KEY"
--source https://api.nuget.org/v3/index.json
--skip-duplicate
73 changes: 73 additions & 0 deletions CONTRIBUTING.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,73 @@
# Contributing to d2lang-cs

Thanks for helping improve `d2lang-cs`. Focused pull requests with tests are the easiest to review and release safely.

## Development setup

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`.

Fork and clone the repository, then create a branch from `main`:

```bash
git switch main
git pull --ff-only
git switch -c feature/short-description
```

Restore, build, and test from the repository root:

```bash
dotnet restore d2lang-cs.sln
dotnet build d2lang-cs.sln --configuration Release --no-restore
dotnet test test/Tests.csproj --configuration Release --no-build
```

Create the NuGet package with SDK package validation enabled:

```bash
dotnet pack src/d2lang-cs.csproj \
--configuration Release \
--output artifacts/packages \
-p:EnablePackageValidation=true
```

## Validate generated D2

Install the [D2 CLI](https://d2lang.com/tour/install/), run the example, and validate its output:

```bash
dotnet run --project example/cli/d2-sample-cli.csproj \
--configuration Release > /tmp/d2lang-cs-example.d2
d2 validate /tmp/d2lang-cs-example.d2
```

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.

## Pull requests

- Keep changes focused on one concern.
- Add or update tests for behavior changes.
- Update the README when public behavior, requirements, or compatibility changes.
- Avoid unrelated formatting or generated-file changes.
- Run the release build, tests, package validation, and relevant D2 validation before requesting review.
- Call out intentional compatibility or output-format changes in the description.

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.

## Release process

Releases are published by `.github/workflows/release.yml`. Maintainers should:

1. Confirm the intended commit is on `main` and CI is green.
2. Confirm the `nuget.org` GitHub environment is protected as desired and contains a `NUGET_API_KEY` secret scoped to the `d2lang-cs` package.
3. Choose an unused semantic version such as `1.2.3` or `1.2.3-rc.1`.
4. Create an annotated `v`-prefixed tag and push it:

```bash
git tag -a v1.2.3 -m "d2lang-cs 1.2.3"
git push origin v1.2.3
```

5. Review the `Publish NuGet package` workflow and its package artifact before confirming the package on NuGet.org.

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.