Visitar URL original
docs(agents): ci.md holds the CI facts; the ci-change skill holds the… · trgiangv/RevitDevTool@47ae662 · GitHub
Skip to content

Commit 47ae662

Browse files
committed
docs(agents): ci.md holds the CI facts; the ci-change skill holds the rules
- ci.md: flow, jobs per product, generated files, caches, timing, diagnostics - ci-change skill: logic in C#, build -- paths, reproduce locally, hermetic tests, cache budget, actionlint before push - build-matrix.md links to ci.md; AGENTS.md and the router name the skill
1 parent 0d62a36 commit 47ae662

5 files changed

Lines changed: 155 additions & 11 deletions

File tree

‎.agents/skills/ci-change/SKILL.md‎

Lines changed: 82 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,82 @@
1+
---
2+
name: ci-change
3+
description: >
4+
Change or debug RevitDevTool CI and release workflows safely. Use whenever a task
5+
touches .github/ (ci.yml, release.yml, product-paths.yml, ci-jobs.json), the CI side
6+
of build/ (Products/CiJobs.cs, ProductPaths.cs, ProjectGraph.cs, Product.cs,
7+
Cli/Targets.cs), adds, moves, or renames a test or source project, investigates a red
8+
`ci / result` or a slow or flaky CI job, or plans a release run, even when the
9+
request only says "the pipeline", "GitHub Actions", or "why did CI fail".
10+
---
11+
12+
# CI Change
13+
14+
Facts (flow, jobs, caches, timing) live in `docs/agents/ci.md`; releases in
15+
`docs/agents/release.md`; policy in ADR 0059. Read `ci.md` first. This skill holds
16+
the working rules, and each one comes from a CI run that went wrong.
17+
18+
## Rules
19+
20+
- **Logic goes in C#, not YAML.** A choice that depends on products, paths, host
21+
versions, or the event belongs in `build/` with a test in `tests/Build.Tests`.
22+
YAML only wires jobs, steps, and keys. Logic in YAML can't be run or tested locally,
23+
so each mistake costs a CI round.
24+
- **Generated files come from `build -- paths`.** Never edit `.github/product-paths.yml`
25+
or `.github/ci-jobs.json` by hand. After you change a project, a `ProjectReference`,
26+
`Product.cs`, `CiJobs.cs`, or a `Release.Autodesk.*` build type, run
27+
`dotnet run --project build -- paths` and commit both files. The test stage refuses
28+
stale copies.
29+
- **Reproduce locally before you push.** For a red job, read its log
30+
(`gh run view <id> --log-failed`), find the root cause, and reproduce it locally with the
31+
same `build` command the job runs (`ci <product>`, `test addin`,
32+
`compile addin --host-version <v>`). A race may need a loop: run the test until it
33+
fails. Push once, with that evidence. Pushing a guess spends a 5–10 minute run and
34+
hides the cause.
35+
- **Tests stay hermetic.** A test that writes HKCU, `%AppData%`, a fixed pipe name, or a
36+
shared file can break on a runner, and only by chance. Inject the key or path instead
37+
(see `docs/agents/test-matrix.md`). Don't make tests run in parallel across projects to
38+
save time: many still share pipes and AppData.
39+
- **Respect the cache budget.** Each matrix job owns `nuget-<cache>-<hash>`, and only a
40+
push saves it. The repository has 10 GB, so don't add a shared or extra cache. A new
41+
job gets its cache name from `CiJobs.cs`.
42+
- **Keep `result` the one required check.** A new job goes into `result.needs`, so a
43+
failure or cancellation still turns `result` red.
44+
- **Pin actions by SHA** with the version in a comment, as the existing steps do.
45+
Give each job `permissions:` and the least it needs. zizmor checks both.
46+
- **Don't add exit-code checks.** The runner's pwsh already exits with the last command's
47+
code, so `if ($LASTEXITCODE ...)` is noise.
48+
49+
## Before pushing
50+
51+
1. `dotnet run --project build -- paths` if any input of the generated files changed.
52+
2. `dotnet run --project tests/Build.Tests/Build.Tests.csproj` after a `build/` change.
53+
3. `docker run --rm -v "${PWD}:/repo" -w /repo rhysd/actionlint:1.7.12 -color` after a
54+
`.github/` change. If Docker is not running, ask the user to start it; don't push
55+
unlinted YAML.
56+
4. For a product failure, the local `build` command that failed on CI now passes.
57+
5. `pwsh -File scripts/format-lint.ps1` once, at the end.
58+
6. Fold the fix into the commit it corrects, then push with `--force-with-lease`.
59+
Commit messages: a subject plus 2–5 bullets.
60+
61+
## After pushing
62+
63+
- Watch with `gh run watch <id> --exit-status`, then list per-job times:
64+
`gh run view <id> --json jobs --jq '.jobs[] | "\(.name)\t\(.conclusion)\t\(.startedAt)\t\(.completedAt)"'`.
65+
- A change meant to speed CI up needs a before and after time for the job it targets.
66+
Update the timing table in `ci.md` when it moves.
67+
- A "Base … is not in this clone" bump warning right after a force push is expected.
68+
69+
## Release workflow
70+
71+
`release.yml` runs `release <product>` on one runner, then publishes after an
72+
environment approval (`docs/agents/release.md`). Treat a change there like a CI change,
73+
and also:
74+
75+
- Secrets reach only `publish` and `channels`. `build` stays secret-free.
76+
- Check locally with `dotnet run --project build -- release <product> --dry-run`. Ask
77+
before you start a real release: it publishes outside the repository.
78+
79+
## Update docs
80+
81+
When observable CI behavior changes (jobs, triggers, caches, timing), update
82+
`docs/agents/ci.md`. Update `release.md` for release steps, and the ADR only for policy.

‎AGENTS.md‎

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -31,6 +31,7 @@ and `docs/agents/`. Read only the layer the task needs.
3131
| pytest host tests | `.agents/skills/revit-pytest/SKILL.md` |
3232
| Platform, IPC, or packaging edit | `.agents/skills/platform-change/SKILL.md` |
3333
| Formatting and lint | `docs/agents/formatting-linting.md` |
34+
| CI workflow edit or a failed CI run | `.agents/skills/ci-change/SKILL.md`, `docs/agents/ci.md` |
3435
| Release a product | `docs/agents/release.md` |
3536

3637
## Verify before done

‎docs/agents/build-matrix.md‎

Lines changed: 1 addition & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -40,17 +40,7 @@ through *Y*. Override `RevitVersionMinimal` / `AutoCadVersionMinimal`, or set
4040
- The add-in compile builds the solution with `-t:RevitDevTool;ACadDevTool`, so only the host projects and their references build, with the solution's configuration mapping.
4141
- On one machine host versions stay sequential. Shared projects are solution-mapped to `Debug`/`Release`, so later host versions reuse `bin/<Debug|Release>/<tfm>`.
4242
- Targets are stages: `compile`, `test`, `ci` (compile + test), `pack`, `release` (all three). The add-in compile empties the host `bin/<configuration>` folders first, then collects each host version into `output/hosts/<v>`: `shared` (files both hosts publish, byte-identical) and one folder per bundle (`RevitDevTool.bundle`, `AcadDevTool.bundle`), and writes `output/hosts/<v>.version` with the version it stamped. `pack addin` builds one bundle per host from `shared` plus that host's folder (0061), only from `output/hosts`, at that version, and refuses when a `Release.Autodesk.*` host version is missing or was compiled at another version, so run `compile addin` first.
43-
- `ci.yml` runs each add-in host version on its own runner instead:
44-
45-
| Job | Command |
46-
|-----|---------|
47-
| `Autodesk <v>` | `compile addin --host-version <v>`; on push `output/hosts` uploads as `addin-<v>` |
48-
| `addin tests` | `test addin` |
49-
| `Autodesk bundle` (push) | downloads every `addin-*` into `output/hosts`, then `pack addin` |
50-
51-
The `result` job fails when `changes` did not succeed or any job failed or was cancelled.
52-
53-
`build -- paths` writes the jobs of each product to `.github/ci-jobs.json` (host versions from the `Release.Autodesk.*` build types in `RevitDevTool.slnx`), and the test stage refuses a stale copy as it does `product-paths.yml`. `ci.yml` only looks up the changed products there. Each job keeps its own NuGet cache (`nuget-<cache>-<hash>`) and saves it on a push whose key is new. `test` and `ci` read the bump-warning base from the GitHub event when `--base` is not given. `release.yml` still runs `release addin` on one runner.
43+
- `ci.yml` runs each add-in host version on its own runner instead; jobs, caches, and timing are in [ci.md](ci.md). `release.yml` still runs `release addin` on one runner.
5444
- NUnit, TUnit, and MSTest host packaging builds those runtime projects with `Debug` or `Release`, not `*.Autodesk.YYYY`.
5545
- `CompileBaseModule` builds the first release configuration. `CompileExpandModule` builds the later years, still one at a time. Bundle creation waits for every year.
5646
- The add-in compile of `build/` sets `DeployAddin=false` and `PublishAddin=true`. `Revit.targets` and `AutoCad.targets` both read those two properties; each project imports only one of those files. The bundle zips are packed from `output/hosts`, so `build/` does not copy into `%AppData%\Autodesk\ApplicationPlugins`. A local deploy puts Revit into `RevitDevTool.bundle` and AutoCAD into `AcadDevTool.bundle`, each with its template from `props/<bundle>/PackageContents.xml`. Local host deploy stays `scripts/build-host.ps1`.

‎docs/agents/ci.md‎

Lines changed: 70 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,70 @@
1+
# CI
2+
3+
`ci.yml` checks every PR, merge group, and push to `develop`. `ci / result` is the one required check.
4+
Policy: ADR [0059](../decisions/0059-independent-product-releases-and-one-gate-ci.md). Releases: [release.md](release.md).
5+
How to change CI: the `ci-change` skill.
6+
7+
## Flow
8+
9+
```
10+
changes ──► product (matrix, one runner per job) ──► Autodesk bundle (push, addin)
11+
├─► docs ──► docs-deploy (push)
12+
└─► workflows (actionlint, zizmor)
13+
all ──► result
14+
```
15+
16+
| Job | Runs when | Does |
17+
|-----|-----------|------|
18+
| `changes` | always | `dorny/paths-filter` with `.github/product-paths.yml`, then picks the changed products' jobs from `.github/ci-jobs.json` with one `jq` lookup. `workflow_dispatch` takes every product |
19+
| `product` | a product changed | Restores its NuGet cache, runs `dotnet run --project build -- <run>`; on push saves a new cache key, packs, and uploads |
20+
| `Autodesk bundle` | push, `addin` changed | Downloads every `addin-<v>` into `output/hosts`, then `pack addin` |
21+
| `docs`, `docs-deploy` | `docs/**` changed | Vite+ build; deploys Pages on push |
22+
| `workflows` | `.github/**` changed | actionlint 1.7.12 and zizmor (high) |
23+
| `result` | always | Fails when `changes` did not succeed or any job failed or was cancelled |
24+
25+
## Jobs per product
26+
27+
`.github/ci-jobs.json` (from `build/Products/CiJobs.cs`):
28+
29+
| Product | Job | Command | Cache |
30+
|---------|-----|---------|-------|
31+
| `daemon` | `daemon` | `ci daemon`, then `pack daemon` on push | `daemon` |
32+
| `addin` | `addin tests` | `test addin` | `addin-tests` |
33+
| `addin` | `Autodesk <v>`, one per `Release.Autodesk.*` in `RevitDevTool.slnx` | `compile addin --host-version <v>`; on push uploads `output/hosts` as `addin-<v>` | `autodesk-<v>` |
34+
| `nettest` | `nettest` | `ci nettest`, then `pack nettest` on every event (ADR 0059: the `0.0.0-ci` nupkg) | `nettest` |
35+
| `pytest` | `pytest` | `ci pytest` (pixi), then `pack pytest` on push | `pytest` |
36+
37+
## Generated files
38+
39+
- `dotnet run --project build -- paths` writes `.github/product-paths.yml` (from the project graph in
40+
`build/Products/ProductPaths.cs` and `ProjectGraph.cs`) and `.github/ci-jobs.json`.
41+
- The test stage and `Build.Tests` refuse a stale copy of either, so a missed `paths` run fails CI, not a silent skip.
42+
- Each test project belongs to one product; see [build-matrix.md](build-matrix.md#commands).
43+
- Files that every .NET product lists (`build/**`, `props/**`, `Directory.*.props`, `global.json`, `tests/*`, both
44+
generated files) start every .NET job.
45+
46+
## Caches
47+
48+
- Key `nuget-<cache>-<hash of csproj, Directory.Build.props, Directory.Packages.props, dotnet-tools.json>`, restore
49+
key `nuget-<cache>-`. The bundle job uses `nuget-bundle`.
50+
- Only a push saves, and only when the key is new. PRs restore the `develop` caches.
51+
- The repository has 10 GB of cache; GitHub evicts the least recently used. `release.yml` keeps no cache so it
52+
cannot evict CI's.
53+
54+
## Timing (2026-11-10)
55+
56+
| Run | Wall time |
57+
|-----|-----------|
58+
| Every job, warm caches | about 10 min |
59+
| Every job, cold caches | about 17 min |
60+
| Daemon only | about 4.6 min |
61+
62+
`addin tests` (about 9.6 min) is the longest job: checkout 46 s, restore 37 s, `build` tool 33 s, builds
63+
`Debug.Autodesk.2027` with `-m:1` (2.2 min, Autodesk configurations share one `obj` folder) and `Debug` (1.5 min),
64+
then 16 test projects one at a time (3.8 min). On push the `Autodesk <v>` plus bundle path takes about 8.5 min.
65+
66+
## Diagnostics
67+
68+
- A test past 5 minutes writes a hang dump; a failed job uploads `**/TestResults/*_hang.*` as `diagnostics-<job>`.
69+
- `test` and `ci` read the bump-warning base from `GITHUB_EVENT_PATH` (`build/Tools/GitHubEvent.cs`). After a force push
70+
the old base is not in the clone and the warning says so; that is expected.

‎docs/agents/index.md‎

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -18,6 +18,7 @@ is [`AGENTS.md`](../../AGENTS.md); work shape is [`WORKFLOW.md`](../WORKFLOW.md)
1818
| Revit API and live execute | `revit-developer` skill, `docs/architecture/MCP/workflows.md` | MCP, rvtdocs-mcp |
1919
| Platform, IPC, packaging | `platform-change` skill | `source/DevTools.*`, `build/` |
2020
| Formatting and lint | [formatting-linting.md](formatting-linting.md) | `.editorconfig`, `scripts/format-lint.ps1` |
21+
| Change CI or debug a CI run | `ci-change` skill, [ci.md](ci.md) | `.github/`, `build/Products/`, `build/Cli/` |
2122
| Release a product | [release.md](release.md) | `build/`, `.github/workflows/release.yml` |
2223
| Multi-session work | `docs/plans/active/` | |
2324
| Policy | `docs/decisions/` | |

0 commit comments

Comments
 (0)