Skip to content

ADR 0008 — CI on GitHub Actions, with the gates fanned out in parallel ​

  • Status: Accepted
  • Date: 2026-08-22
  • Supersedes: the platform decision in ADR 0001 (single CI on Azure DevOps). The gate content decided there — SAST, SCA, secret scanning, DAST, mutation testing — is unchanged and still in force.

Context ​

The git repository moved from Azure Repos (dev.azure.com/grydlab/Gryd.IO) to GitHub (github.com/GRYD-LAB/Gryd.IO). azure-pipelines.yml could no longer be triggered by a push or a pull request, so the CI that ADR 0001 consolidated onto Azure DevOps had nowhere to run.

ADR 0001 chose a single CI on Azure DevOps and deleted .github/. That decision was made when the code lived in Azure Repos; the premise no longer holds. Work items, boards and the package feed stay in Azure DevOps.

The Azure pipeline also carried a constraint worth naming, because it shaped the whole design: the organisation had one parallel job. Its own comment on BuildAndTest reads "the cheap gates are deliberately serialized ahead of it instead of racing it […] Restore the parallel fan-out once the org has more than one parallel job." Every gate therefore waited for the one before it, and a pull request paid the sum of all of them. GitHub-hosted runners give this account 20 concurrent jobs.

Decision ​

1. GitHub Actions is the CI platform ​

azure-pipelines.yml is deleted and replaced by five entry-point workflows plus three reusable ones, mapped stage by stage in .github/workflows/README.md. No gate is dropped: branch-flow rules, commitlint, SCA, secret scanning, the tracked-artifact and banned-identifier guards, build, test, coverage, SonarCloud, nightly tests, mutation testing, performance smoke, DAST, NuGet pack/publish and both docs deploys all survive.

2. The gates run in parallel ​

The chain branch rules → commitlint → security → build becomes a fan-out. Nothing about it was a correctness requirement; it was queue economics on a single agent. A failing commitlint now costs a cancelled build instead of a delayed one, which is the right trade when the build does not have to wait for a free agent.

3. The test suite is sharded ​

dotnet test Gryd.IO.sln becomes ten parallel jobs over the shards in .github/test-shards.json. Wall-clock is now the slowest shard — the Auth integration tests — instead of every project in sequence.

A hand-maintained list of test projects rots silently: someone adds a test project, forgets the shard file, and CI stays green while never running it. .github/scripts/check-test-shards.sh therefore runs before the matrix and fails when the shards and the repository disagree, in either direction.

4. Packages continue to publish to Azure Artifacts ​

The feed does not move. The only change is authentication: NuGetAuthenticate@1 used the Azure DevOps build identity, which does not exist on GitHub, so the push authenticates with a PAT (AZURE_ARTIFACTS_PAT) written into a throwaway nuget.config. The repository's own nuget.config starts with <clear />, which would otherwise drop the push source.

5. Reporting is rebuilt, not dropped ​

PublishTestResults@2 and PublishCodeCoverageResults@2 have no GitHub equivalent. Test results are parsed from the .trx files into the run summary (.github/scripts/trx-summary.py), and the ten per-shard Cobertura files are merged with ReportGenerator into one report and one summary.

6. Branch policy (manual, GitHub settings) ​

As in ADR 0001, the pipeline exposes the gates and the enforcement is configured outside the repo: Settings → Branches → develop, requiring the PR gate check. Matrix jobs and a reusable workflow's inner jobs cannot be named as required checks, so PR gate exists to aggregate them into a single required name.

Consequences ​

  • Pull request feedback is bounded by the slowest job rather than the sum of the gates, and a broken commit message no longer blocks the build behind it.
  • More total CI minutes are consumed: shards rebuild the shared projects they each depend on. This is the trade the 20-job concurrency buys, and it is the intended one.
  • The shard list is a new thing to maintain. It is guarded by a script rather than by discipline.
  • One new secret (AZURE_ARTIFACTS_PAT) has to be rotated. Azure's build identity needed no rotation; that convenience does not survive leaving the platform.
  • Work items and the package feed still live in Azure DevOps, so the two systems have not fully parted ways — only CI has.

Updated at:

Released under the MIT License.