@cratis/pi 7.100.0 → 7.102.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/package/corpus/agents/backend-developer.md +1 -0
- package/package/corpus/agents/browser-operator.md +44 -0
- package/package/corpus/agents/code-reviewer.md +4 -0
- package/package/corpus/agents/coordinator.md +1 -0
- package/package/corpus/agents/frontend-developer.md +1 -0
- package/package/corpus/agents/orchestrator.md +1 -0
- package/package/corpus/agents/performance-reviewer.md +4 -0
- package/package/corpus/agents/planner.md +1 -0
- package/package/corpus/agents/repository-investigation-reviewer.md +4 -0
- package/package/corpus/agents/repository-investigator.md +1 -0
- package/package/corpus/agents/screenplay-modeler.md +1 -0
- package/package/corpus/agents/screenplay-renderer.md +1 -0
- package/package/corpus/agents/screenplay-reviewer.md +4 -0
- package/package/corpus/agents/security-reviewer.md +4 -0
- package/package/corpus/agents/slice-implementer.md +1 -0
- package/package/corpus/agents/spec-writer.md +1 -0
- package/package/corpus/harnesses/copilot/instructions/browser.instructions.md +46 -0
- package/package/corpus/harnesses/copilot/instructions/github-actions.instructions.md +17 -0
- package/package/corpus/harnesses/copilot/instructions/local-work-artifacts.instructions.md +3 -0
- package/package/corpus/harnesses/copilot/instructions/review-reports.instructions.md +11 -0
- package/package/corpus/harnesses/copilot/instructions/secrets.instructions.md +35 -0
- package/package/corpus/harnesses/pi/agents/backend-developer.md +1 -0
- package/package/corpus/harnesses/pi/agents/browser-operator.md +41 -0
- package/package/corpus/harnesses/pi/agents/code-reviewer.md +4 -0
- package/package/corpus/harnesses/pi/agents/coordinator.md +1 -0
- package/package/corpus/harnesses/pi/agents/frontend-developer.md +1 -0
- package/package/corpus/harnesses/pi/agents/orchestrator.md +1 -0
- package/package/corpus/harnesses/pi/agents/performance-reviewer.md +4 -0
- package/package/corpus/harnesses/pi/agents/planner.md +1 -0
- package/package/corpus/harnesses/pi/agents/repository-investigation-reviewer.md +4 -0
- package/package/corpus/harnesses/pi/agents/repository-investigator.md +1 -0
- package/package/corpus/harnesses/pi/agents/screenplay-modeler.md +1 -0
- package/package/corpus/harnesses/pi/agents/screenplay-renderer.md +1 -0
- package/package/corpus/harnesses/pi/agents/screenplay-reviewer.md +4 -0
- package/package/corpus/harnesses/pi/agents/security-reviewer.md +4 -0
- package/package/corpus/harnesses/pi/agents/slice-implementer.md +1 -0
- package/package/corpus/harnesses/pi/agents/spec-writer.md +1 -0
- package/package/corpus/harnesses/pi/extensions/cratis-hooks/index.ts +19 -6
- package/package/corpus/harnesses/pi/extensions/cratis-hooks/quality-gate.ts +10 -1
- package/package/corpus/hooks/README.md +8 -0
- package/package/corpus/hooks/scripts/cratis-guard-secrets.sh +458 -0
- package/package/corpus/hooks/settings.template.json +4 -0
- package/package/corpus/rules/browser.md +45 -0
- package/package/corpus/rules/general.md +2 -0
- package/package/corpus/rules/github-actions.md +17 -0
- package/package/corpus/rules/local-work-artifacts.md +3 -0
- package/package/corpus/rules/review-reports.md +11 -0
- package/package/corpus/rules/secrets.md +34 -0
- package/package/corpus/skills/cratis-ci-pipeline-performance/LICENSE +2 -0
- package/package/corpus/skills/cratis-ci-pipeline-performance/SKILL.md +201 -0
- package/package/corpus/skills/cratis-ci-pipeline-performance/references/build-once-and-shard.md +158 -0
- package/package/corpus/skills/cratis-ci-pipeline-performance/references/caching.md +86 -0
- package/package/corpus/skills/cratis-ci-pipeline-performance/references/flaky-and-hung-jobs.md +70 -0
- package/package/corpus/skills/cratis-ci-pipeline-performance/references/publish-pipelines.md +67 -0
- package/package/corpus/skills/cratis-ci-pipeline-performance/references/required-checks-and-path-filters.md +117 -0
- package/package/corpus/skills/cratis-ci-pipeline-performance/references/runner-sizing-and-images.md +65 -0
- package/package/corpus/skills/cratis-ci-pipeline-performance/references/security-and-pinning.md +73 -0
- package/package/corpus/skills/cratis-ci-pipeline-performance/references/sources.md +33 -0
- package/package/corpus/skills/cratis-ci-pipeline-performance/references/test-setup-cost.md +82 -0
- package/package/corpus/skills/cratis-ci-pipeline-performance/scripts/ci-timings.sh +356 -0
- package/package/corpus/skills/cratis-ci-pipeline-performance/scripts/fixtures/jobs/1003.json +79 -0
- package/package/corpus/skills/cratis-ci-pipeline-performance/scripts/fixtures/jobs/1007.json +79 -0
- package/package/corpus/skills/cratis-ci-pipeline-performance/scripts/fixtures/jobs/1010.json +79 -0
- package/package/corpus/skills/cratis-ci-pipeline-performance/scripts/fixtures/runs.json +102 -0
- package/package/corpus/skills/cratis-engineering-local-cross-repo-verification/LICENSE +2 -0
- package/package/corpus/skills/cratis-engineering-local-cross-repo-verification/SKILL.md +244 -0
- package/package/corpus/skills/cratis-engineering-local-cross-repo-verification/references/docker-images.md +86 -0
- package/package/corpus/skills/cratis-engineering-local-cross-repo-verification/references/dotnet-nuget.md +87 -0
- package/package/corpus/skills/cratis-engineering-local-cross-repo-verification/references/go-jvm-python-elixir.md +83 -0
- package/package/corpus/skills/cratis-engineering-local-cross-repo-verification/references/npm-yarn.md +78 -0
- package/package/corpus/skills/cratis-engineering-local-cross-repo-verification/references/other-environments.md +60 -0
- package/package/corpus/skills/cratis-engineering-local-cross-repo-verification/references/worked-examples.md +97 -0
- package/package/corpus/skills/cratis-engineering-local-cross-repo-verification/scripts/cratis-local-packages.sh +778 -0
- package/package/corpus/skills/cratis-engineering-local-cross-repo-verification/scripts/npm-restore-check.cjs +109 -0
- package/package/profile-catalog.json +2 -0
- package/package.json +3 -2
|
@@ -0,0 +1,117 @@
|
|
|
1
|
+
# Required checks, path filters and merge queue
|
|
2
|
+
|
|
3
|
+
## Contents
|
|
4
|
+
|
|
5
|
+
- The hang
|
|
6
|
+
- Change detection and the aggregator
|
|
7
|
+
- Keeping required checks stable
|
|
8
|
+
- Merge queue
|
|
9
|
+
|
|
10
|
+
Use this when skipping work by changed path, when a pull request hangs on a check
|
|
11
|
+
that never reports, or when pull requests that pass alone break the default branch
|
|
12
|
+
together.
|
|
13
|
+
|
|
14
|
+
## The hang
|
|
15
|
+
|
|
16
|
+
A required status check that never reports blocks the merge forever. A workflow
|
|
17
|
+
that has a workflow-level `paths:` filter does not start when no path matches, so
|
|
18
|
+
its required check stays pending. A job skipped through `if:` or `needs` reports
|
|
19
|
+
`skipped`, which counts as passing. So: skip jobs, never whole required
|
|
20
|
+
workflows.
|
|
21
|
+
|
|
22
|
+
## Change detection and the aggregator
|
|
23
|
+
|
|
24
|
+
Run a cheap `changes` job, gate heavy jobs on its outputs, and require one
|
|
25
|
+
always-run aggregator.
|
|
26
|
+
|
|
27
|
+
```yaml
|
|
28
|
+
jobs:
|
|
29
|
+
changes:
|
|
30
|
+
runs-on: ubuntu-latest
|
|
31
|
+
timeout-minutes: 5
|
|
32
|
+
permissions:
|
|
33
|
+
contents: read
|
|
34
|
+
outputs:
|
|
35
|
+
code: ${{ steps.diff.outputs.code }}
|
|
36
|
+
steps:
|
|
37
|
+
- uses: actions/checkout@v7 # pin by full commit SHA
|
|
38
|
+
with:
|
|
39
|
+
fetch-depth: 2
|
|
40
|
+
- id: diff
|
|
41
|
+
run: |
|
|
42
|
+
if git diff --name-only HEAD^1 HEAD | grep -q -v -E '^(Documentation/|.*\.md$)'; then
|
|
43
|
+
echo "code=true" >> "$GITHUB_OUTPUT"
|
|
44
|
+
else
|
|
45
|
+
echo "code=false" >> "$GITHUB_OUTPUT"
|
|
46
|
+
fi
|
|
47
|
+
|
|
48
|
+
build:
|
|
49
|
+
needs: [changes]
|
|
50
|
+
if: needs.changes.outputs.code == 'true'
|
|
51
|
+
runs-on: ubuntu-latest
|
|
52
|
+
timeout-minutes: 45
|
|
53
|
+
steps:
|
|
54
|
+
- run: echo build
|
|
55
|
+
|
|
56
|
+
checks:
|
|
57
|
+
if: always()
|
|
58
|
+
needs: [changes, build]
|
|
59
|
+
runs-on: ubuntu-latest
|
|
60
|
+
timeout-minutes: 5
|
|
61
|
+
permissions: {}
|
|
62
|
+
steps:
|
|
63
|
+
- name: Require every needed job to pass or be skipped by design
|
|
64
|
+
env:
|
|
65
|
+
RESULTS: ${{ toJSON(needs.*.result) }}
|
|
66
|
+
run: |
|
|
67
|
+
echo "$RESULTS"
|
|
68
|
+
if echo "$RESULTS" | jq -e 'any(. == "failure" or . == "cancelled")' > /dev/null; then
|
|
69
|
+
exit 1
|
|
70
|
+
fi
|
|
71
|
+
```
|
|
72
|
+
|
|
73
|
+
- Require only `checks` in branch protection. Adding a job to the workflow then
|
|
74
|
+
means adding it to `needs`; nothing else changes.
|
|
75
|
+
- `if: always()` is essential: without it the aggregator is skipped when a needed
|
|
76
|
+
job fails, and a skipped required check counts as passing.
|
|
77
|
+
- List every job in `needs`. `needs.*.result` covers direct needs only, and a
|
|
78
|
+
job missing from the list can fail unnoticed.
|
|
79
|
+
- The aggregator itself needs a runner, so it adds one scheduling boundary; keep
|
|
80
|
+
it on a cheap runner with a short timeout.
|
|
81
|
+
- Keep workflow-level `paths:` for workflows nothing requires (a docs site
|
|
82
|
+
build). For one event, `paths` and `paths-ignore` cannot be combined; use
|
|
83
|
+
`paths` with `!` negation patterns.
|
|
84
|
+
|
|
85
|
+
## Keeping required checks stable
|
|
86
|
+
|
|
87
|
+
Branch protection matches check names. Renaming a job, changing a matrix key or
|
|
88
|
+
moving a job to another workflow removes the old check and leaves the rule waiting
|
|
89
|
+
on a name that no longer exists. When editing, list the check names before and
|
|
90
|
+
after the change and keep them equal, or update the protection rule in the same
|
|
91
|
+
step.
|
|
92
|
+
|
|
93
|
+
Never make a required check conditional on the thing it validates: release,
|
|
94
|
+
schema, migration and shared-library checks run on every pull request that can
|
|
95
|
+
affect them. If the filter must be wide to be safe, keep it wide.
|
|
96
|
+
|
|
97
|
+
## Merge queue
|
|
98
|
+
|
|
99
|
+
Fits when pull requests pass individually and break after merge (semantic
|
|
100
|
+
conflicts), and the cost of a broken default branch is higher than the extra
|
|
101
|
+
validation. It does not shorten CI; it adds a validation of the queued result.
|
|
102
|
+
|
|
103
|
+
```yaml
|
|
104
|
+
on:
|
|
105
|
+
pull_request:
|
|
106
|
+
merge_group:
|
|
107
|
+
types: [checks_requested]
|
|
108
|
+
```
|
|
109
|
+
|
|
110
|
+
- The required workflows must run on `merge_group`, or the queue waits forever for
|
|
111
|
+
a check that never appears.
|
|
112
|
+
- The aggregator must handle both events. For change detection on `merge_group`,
|
|
113
|
+
compare against `github.event.merge_group.base_sha` instead of `HEAD^1`.
|
|
114
|
+
- Concurrency groups must keep merge-group runs apart from pull-request runs, and
|
|
115
|
+
must not cancel a merge-group run for a newer pull-request push.
|
|
116
|
+
- Build concurrency, merge-group batching and the queue's own settings are
|
|
117
|
+
separate controls; change them one at a time.
|
package/package/corpus/skills/cratis-ci-pipeline-performance/references/runner-sizing-and-images.md
ADDED
|
@@ -0,0 +1,65 @@
|
|
|
1
|
+
# Runner sizing, images and cache layers
|
|
2
|
+
|
|
3
|
+
Use this when every job is uniformly slow, when jobs spend time pulling or
|
|
4
|
+
installing tools, or when deciding how to warm workspaces.
|
|
5
|
+
|
|
6
|
+
## Contents
|
|
7
|
+
|
|
8
|
+
- Cores per job before job count
|
|
9
|
+
- Container images
|
|
10
|
+
- Tools baked into the runner image
|
|
11
|
+
- Cache layers and warm workspaces
|
|
12
|
+
- Publish stages
|
|
13
|
+
|
|
14
|
+
## Cores per job before job count
|
|
15
|
+
|
|
16
|
+
Undersized runner pods (a small CPU request or limit) make every job slow, and more
|
|
17
|
+
jobs then multiply the slowness. Before sharding:
|
|
18
|
+
|
|
19
|
+
1. Print `nproc` and the memory limit at the start of a slow job, and read the
|
|
20
|
+
runner's resource configuration.
|
|
21
|
+
2. If compile or test steps scale with cores, a larger runner or a higher CPU
|
|
22
|
+
request is the first lever. Runner capacity is an organization setting; request
|
|
23
|
+
it from the owner with the measured evidence, do not work around it in YAML.
|
|
24
|
+
3. Re-measure with the same `ci-timings.sh` command.
|
|
25
|
+
|
|
26
|
+
## Container images
|
|
27
|
+
|
|
28
|
+
- Start only the services a job uses; pull only those images. Service containers
|
|
29
|
+
that a pull-request job never touches are pure wait.
|
|
30
|
+
- Pull images in parallel (`docker pull a & docker pull b & wait`, checking each
|
|
31
|
+
exit status) rather than serially.
|
|
32
|
+
- Do not pull a large release image to obtain one small tool; install or copy the
|
|
33
|
+
tool directly.
|
|
34
|
+
- Avoid re-pulling a large handoff image in every downstream job; see publish
|
|
35
|
+
stages below.
|
|
36
|
+
|
|
37
|
+
## Tools baked into the runner image
|
|
38
|
+
|
|
39
|
+
Browsers, system packages and language toolchains installed on every run cost
|
|
40
|
+
minutes every run. Bake them into the runner image and keep the install step only as
|
|
41
|
+
a version check. Keep the image's contents versioned so a change is reviewable.
|
|
42
|
+
|
|
43
|
+
## Cache layers and warm workspaces
|
|
44
|
+
|
|
45
|
+
Prefer content-addressed caches, which are correct by construction, before
|
|
46
|
+
warm volumes:
|
|
47
|
+
|
|
48
|
+
1. A registry mirror or pull-through cache for images.
|
|
49
|
+
2. A remote or registry-backed BuildKit cache (see `caching.md`).
|
|
50
|
+
3. Package caches keyed by lock files.
|
|
51
|
+
|
|
52
|
+
Warm workspaces (a prepared checkout and build output on a volume) are only safe
|
|
53
|
+
when they come from a snapshot of a known-good state. Round-robin reuse of
|
|
54
|
+
whichever runner last ran leaves stale or poisoned output. Publish and release
|
|
55
|
+
builds stay cold: a clean build is part of what makes the artifact trustworthy.
|
|
56
|
+
|
|
57
|
+
## Publish stages
|
|
58
|
+
|
|
59
|
+
- Run independent stages in parallel, for example build alongside a dependency
|
|
60
|
+
audit, and join before publishing.
|
|
61
|
+
- Build several images with `docker buildx bake` and a registry cache instead of
|
|
62
|
+
one serial build per image.
|
|
63
|
+
- When one job builds an image that later jobs consume, keep the handoff small or
|
|
64
|
+
rebuild from cache, whichever the slowest-steps table shows is cheaper.
|
|
65
|
+
- Keep the publish-path guardrails in `publish-pipelines.md`.
|
package/package/corpus/skills/cratis-ci-pipeline-performance/references/security-and-pinning.md
ADDED
|
@@ -0,0 +1,73 @@
|
|
|
1
|
+
# Security alongside speed
|
|
2
|
+
|
|
3
|
+
Use this when a speed change adds or updates an action, edits `permissions:`,
|
|
4
|
+
changes a trigger, or adds a cache. Speed changes widen the blast radius of a
|
|
5
|
+
workflow more often than they narrow it. Run `zizmor` (security audits, including
|
|
6
|
+
`unpinned-uses` and `excessive-permissions`) and `actionlint` (syntax, expressions,
|
|
7
|
+
embedded shell; it does not check pinning) locally on every edited workflow when
|
|
8
|
+
installed. Do not assume CI runs them.
|
|
9
|
+
|
|
10
|
+
## Contents
|
|
11
|
+
|
|
12
|
+
- Pin third-party actions
|
|
13
|
+
- Minimal permissions
|
|
14
|
+
- Untrusted code and triggers
|
|
15
|
+
- Caches and artifacts
|
|
16
|
+
- Verification
|
|
17
|
+
|
|
18
|
+
## Pin third-party actions
|
|
19
|
+
|
|
20
|
+
A tag can be moved to different code; a full commit SHA cannot. Pin every
|
|
21
|
+
third-party action by its 40-character commit SHA and keep the version in a
|
|
22
|
+
comment so updates remain reviewable:
|
|
23
|
+
|
|
24
|
+
```yaml
|
|
25
|
+
- uses: actions/checkout@<40-character-sha> # v7
|
|
26
|
+
```
|
|
27
|
+
|
|
28
|
+
- Resolve a tag to its commit with `gh api repos/OWNER/REPO/commits/TAG --jq .sha`
|
|
29
|
+
and confirm it matches the release you intend.
|
|
30
|
+
- Examples in this skill show a major tag with a "pin by full commit SHA" note;
|
|
31
|
+
the major shown is not a recommendation to pin a tag.
|
|
32
|
+
- Actions of the `Cratis` organization follow the owning repository's convention.
|
|
33
|
+
- Let a dependency-update tool bump pinned SHAs so pinning does not freeze them.
|
|
34
|
+
|
|
35
|
+
## Minimal permissions
|
|
36
|
+
|
|
37
|
+
- Set `permissions:` at the job level, not the workflow level, and grant only
|
|
38
|
+
what the job uses. Start from `contents: read`, or `permissions: {}` for a job
|
|
39
|
+
that touches no repository content.
|
|
40
|
+
- Grant write scopes (`contents`, `packages`, `pull-requests`, `id-token`) only to
|
|
41
|
+
the job that needs them, such as the publisher.
|
|
42
|
+
- Do not use a personal access token where the workflow token suffices.
|
|
43
|
+
|
|
44
|
+
## Untrusted code and triggers
|
|
45
|
+
|
|
46
|
+
- Never check out or run code from a pull request under `pull_request_target`
|
|
47
|
+
or `workflow_run` with the base repository's credentials: it exposes secrets
|
|
48
|
+
and a write token to fork code.
|
|
49
|
+
- `pull_request` from a fork has no secrets, which is the safe default for
|
|
50
|
+
verification.
|
|
51
|
+
- Never route `pull_request` jobs of a public repository to self-hosted runners.
|
|
52
|
+
- Pass untrusted values (branch names, titles, comment bodies) to scripts through
|
|
53
|
+
`env:`, never interpolated into `run:` with `${{ }}`.
|
|
54
|
+
|
|
55
|
+
## Caches and artifacts
|
|
56
|
+
|
|
57
|
+
- Caches must not contain secrets. A cache saved from the default branch is
|
|
58
|
+
readable by every branch; a cache saved by a pull request is scoped to it.
|
|
59
|
+
- Do not restore a pull-request cache in a privileged workflow.
|
|
60
|
+
- Use `persist-credentials: false` on `actions/checkout` when the job does not
|
|
61
|
+
push, so the token is not left in the checkout.
|
|
62
|
+
- Artifacts are not private from other jobs in the run; do not put credentials
|
|
63
|
+
in them.
|
|
64
|
+
|
|
65
|
+
## Verification
|
|
66
|
+
|
|
67
|
+
```bash
|
|
68
|
+
actionlint .github/workflows/*.yml
|
|
69
|
+
zizmor .github/workflows/
|
|
70
|
+
```
|
|
71
|
+
|
|
72
|
+
Pass means exit `0` and no findings. Report a tool that is not installed as not
|
|
73
|
+
run. Resolve findings in the workflow; suppress one only with a recorded reason.
|
|
@@ -0,0 +1,33 @@
|
|
|
1
|
+
# Sources and attribution
|
|
2
|
+
|
|
3
|
+
The structure and method of this skill adapt two MIT-licensed skills. The text
|
|
4
|
+
here is new; no passage is copied.
|
|
5
|
+
|
|
6
|
+
- `github-actions-efficiency` from github/awesome-copilot, MIT license:
|
|
7
|
+
https://github.com/github/awesome-copilot/blob/main/skills/github-actions-efficiency/SKILL.md.
|
|
8
|
+
Adapted: measure before changing, guardrails against hiding required
|
|
9
|
+
validation, separating repository changes from organization settings, a
|
|
10
|
+
static-only analysis label, expected against measured savings, and wall clock
|
|
11
|
+
reported apart from runner-minutes.
|
|
12
|
+
- `ci-speedup` from starslingdev/skills, MIT license:
|
|
13
|
+
https://github.com/starslingdev/skills/blob/main/skills/ci-speedup/SKILL.md.
|
|
14
|
+
Adapted: the critical path over required checks, queue delay as a separate
|
|
15
|
+
signal, shard imbalance, and an always-run aggregator that checks every
|
|
16
|
+
`needs.*.result`, including for `merge_group`.
|
|
17
|
+
|
|
18
|
+
Primary documentation consulted for factual claims:
|
|
19
|
+
|
|
20
|
+
- GitHub Docs, dependency caching, secure use of Actions, concurrency control and
|
|
21
|
+
merge queue.
|
|
22
|
+
- Docker Docs, GitHub Actions cache backend (custom `scope`).
|
|
23
|
+
- Microsoft Learn, `dotnet test` blame options and Microsoft.Testing.Platform
|
|
24
|
+
retry and crash/hang extensions.
|
|
25
|
+
- The `actionlint` and `zizmor` project documentation.
|
|
26
|
+
|
|
27
|
+
- Studio CI investigation (Einar Ingebrigtsen, Cratis, 2026): the method of counting
|
|
28
|
+
compiles per push, sizing cores before job count, finding test time hidden outside
|
|
29
|
+
per-test timing, testing each guarantee where it can break, and layering
|
|
30
|
+
content-addressed caches before warm workspaces. Reduced here to generic levers.
|
|
31
|
+
|
|
32
|
+
The Cratis-specific levers come from the shared `Cratis/Workflows` cache actions
|
|
33
|
+
and the build-once, artifact and shard layout used in the Arc repository.
|
|
@@ -0,0 +1,82 @@
|
|
|
1
|
+
# Test setup cost and where to test a guarantee
|
|
2
|
+
|
|
3
|
+
Use this when a test lane is slow but the runner's per-test times look small, when
|
|
4
|
+
a suite re-tests a dependency, or when compile errors surface late.
|
|
5
|
+
|
|
6
|
+
## Contents
|
|
7
|
+
|
|
8
|
+
- Time hidden outside per-test timing
|
|
9
|
+
- Profile the test host
|
|
10
|
+
- Fix setup upstream
|
|
11
|
+
- Test each guarantee where it can break
|
|
12
|
+
- Surface compile errors early
|
|
13
|
+
- Count compiles per push
|
|
14
|
+
|
|
15
|
+
## Time hidden outside per-test timing
|
|
16
|
+
|
|
17
|
+
Compare the test runner's reported total test time with the wall time of the test
|
|
18
|
+
step. A large gap means time is spent outside what the runner attributes to tests:
|
|
19
|
+
per-class or per-fixture setup (scenario, host or container construction), discovery,
|
|
20
|
+
or process start. Specification frameworks that build a context in a base-class
|
|
21
|
+
setup often charge it to no test at all.
|
|
22
|
+
|
|
23
|
+
1. Read both numbers from the same run: the runner's summary and the step duration
|
|
24
|
+
from `ci-timings.sh`'s slowest-steps table.
|
|
25
|
+
2. If setup dominates, sharding only divides the waste across more jobs. Fix the
|
|
26
|
+
setup first.
|
|
27
|
+
|
|
28
|
+
## Profile the test host
|
|
29
|
+
|
|
30
|
+
Measure before guessing, with the language's profiler on the test process:
|
|
31
|
+
|
|
32
|
+
```bash
|
|
33
|
+
dotnet tool install --global dotnet-trace
|
|
34
|
+
dotnet-trace collect --format speedscope -- dotnet test Project.Specs --no-build --configuration Release
|
|
35
|
+
```
|
|
36
|
+
|
|
37
|
+
Read the inclusive time of the setup methods. Pass means the profile names the
|
|
38
|
+
dominant constructor or discovery call; a flat profile means the cost is elsewhere
|
|
39
|
+
(I/O, container start, process start).
|
|
40
|
+
|
|
41
|
+
## Fix setup upstream
|
|
42
|
+
|
|
43
|
+
Per-test construction of identical, immutable data is the usual cause. Fix it where
|
|
44
|
+
the data originates rather than skipping specifications:
|
|
45
|
+
|
|
46
|
+
- Build shared metadata (type discovery, schemas, registrations) once per process
|
|
47
|
+
and reuse it across scenarios.
|
|
48
|
+
- Share expensive generators and caches instead of creating one per instance.
|
|
49
|
+
- Cache reflection results that are recomputed on every call.
|
|
50
|
+
- Use a prebuilt service collection as a template and copy it per test.
|
|
51
|
+
- Do not share mutable state between tests to save time; share only what is immutable.
|
|
52
|
+
|
|
53
|
+
A fix belongs in the library that owns the setup, not in each consuming repository.
|
|
54
|
+
|
|
55
|
+
## Test each guarantee where it can break
|
|
56
|
+
|
|
57
|
+
- A suite that tests a dependency's semantics belongs in that dependency's own
|
|
58
|
+
repository. In a consumer, run it only when the dependency pin changes, or nightly.
|
|
59
|
+
- Check a wire or API shape with a committed snapshot and a diff, not a live
|
|
60
|
+
integration run.
|
|
61
|
+
- Mixed-version or upgrade matrices run nightly or before a release, not on every
|
|
62
|
+
pull request.
|
|
63
|
+
- Where a behavior is only meaningful in a deployed environment, prefer a
|
|
64
|
+
post-deploy synthetic check plus unit specifications and contract snapshots over
|
|
65
|
+
a pull-request integration environment.
|
|
66
|
+
- Never remove the only check of a required guarantee; relocate it and say where it
|
|
67
|
+
now runs.
|
|
68
|
+
|
|
69
|
+
## Surface compile errors early
|
|
70
|
+
|
|
71
|
+
A pull request that fails to compile should fail in the first minutes, not after a
|
|
72
|
+
long test leg. Make a fast build or lint job a `needs` of the long legs, and keep
|
|
73
|
+
it short enough that it is not itself the critical path.
|
|
74
|
+
|
|
75
|
+
## Count compiles per push
|
|
76
|
+
|
|
77
|
+
Count how many times one push compiles the same code across all jobs and
|
|
78
|
+
workflows (`dotnet build` or `dotnet test` without `--no-build` in the step list).
|
|
79
|
+
Each extra compile is avoidable: build once, reuse the output (see
|
|
80
|
+
`build-once-and-shard.md`). Analyzers can roughly double compile time; run them once
|
|
81
|
+
in a parallel lint job and disable them in the shared build, while keeping the
|
|
82
|
+
strictest configuration that consumers enforce somewhere on the required path.
|