@cratis/pi 7.101.0 → 7.103.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/harnesses/copilot/instructions/github-actions.instructions.md +17 -0
- package/package/corpus/harnesses/copilot/instructions/profiles.instructions.md +10 -1
- package/package/corpus/harnesses/copilot/instructions/pull-requests.instructions.md +2 -0
- package/package/corpus/harnesses/copilot/instructions/secrets.instructions.md +4 -0
- package/package/corpus/hooks/README.md +11 -1
- package/package/corpus/hooks/scripts/cratis-check-pr.mjs +12 -1
- package/package/corpus/hooks/scripts/cratis-guard-secrets.sh +79 -23
- package/package/corpus/hooks/scripts/cratis-guard-writes.sh +145 -40
- package/package/corpus/rules/github-actions.md +17 -0
- package/package/corpus/rules/profiles.md +10 -1
- package/package/corpus/rules/pull-requests.md +2 -0
- package/package/corpus/rules/secrets.md +4 -0
- package/package/corpus/skills/analyzing-dotnet-performance/LICENSE +23 -0
- package/package/corpus/skills/analyzing-dotnet-performance/NOTICE +36 -0
- package/package/corpus/skills/analyzing-dotnet-performance/SKILL.md +214 -0
- package/package/corpus/skills/analyzing-dotnet-performance/references/async-patterns.md +122 -0
- package/package/corpus/skills/analyzing-dotnet-performance/references/collections-and-linq.md +220 -0
- package/package/corpus/skills/analyzing-dotnet-performance/references/critical-patterns.md +291 -0
- package/package/corpus/skills/analyzing-dotnet-performance/references/io-and-serialization.md +135 -0
- package/package/corpus/skills/analyzing-dotnet-performance/references/memory-and-strings.md +219 -0
- package/package/corpus/skills/analyzing-dotnet-performance/references/regex-patterns.md +95 -0
- package/package/corpus/skills/analyzing-dotnet-performance/references/structural-patterns.md +38 -0
- package/package/corpus/skills/binlog-generation/LICENSE +23 -0
- package/package/corpus/skills/binlog-generation/NOTICE +22 -0
- package/package/corpus/skills/binlog-generation/SKILL.md +129 -0
- package/package/corpus/skills/check-bin-obj-clash/LICENSE +23 -0
- package/package/corpus/skills/check-bin-obj-clash/NOTICE +27 -0
- package/package/corpus/skills/check-bin-obj-clash/SKILL.md +127 -0
- package/package/corpus/skills/check-bin-obj-clash/references/common-causes-and-fixes.md +206 -0
- package/package/corpus/skills/coverage-analysis/LICENSE +23 -0
- package/package/corpus/skills/coverage-analysis/NOTICE +32 -0
- package/package/corpus/skills/coverage-analysis/SKILL.md +198 -0
- package/package/corpus/skills/coverage-analysis/references/guidelines.md +68 -0
- package/package/corpus/skills/coverage-analysis/references/output-format.md +91 -0
- package/package/corpus/skills/coverage-analysis/references/report-generation.md +58 -0
- package/package/corpus/skills/coverage-analysis/references/setup-discovery.md +307 -0
- package/package/corpus/skills/coverage-analysis/scripts/Compute-CrapScores.ps1 +80 -0
- package/package/corpus/skills/coverage-analysis/scripts/CoverageData.ps1 +272 -0
- package/package/corpus/skills/coverage-analysis/scripts/Extract-MethodCoverage.ps1 +65 -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/corpus/skills/csharp-refactoring/LICENSE +23 -0
- package/package/corpus/skills/csharp-refactoring/NOTICE +22 -0
- package/package/corpus/skills/csharp-refactoring/SKILL.md +175 -0
- package/package/corpus/skills/csharp-refactoring/references/operation-catalog.md +37 -0
- package/package/corpus/skills/directory-build-organization/LICENSE +23 -0
- package/package/corpus/skills/directory-build-organization/NOTICE +21 -0
- package/package/corpus/skills/directory-build-organization/SKILL.md +205 -0
- package/package/corpus/skills/directory-build-organization/references/common-patterns.md +56 -0
- package/package/corpus/skills/directory-build-organization/references/multi-level-examples.md +167 -0
- package/package/corpus/skills/directory-build-organization/references/targetframework-props-pitfall.md +54 -0
- package/package/corpus/skills/dotnet-aot-compat/LICENSE +23 -0
- package/package/corpus/skills/dotnet-aot-compat/NOTICE +18 -0
- package/package/corpus/skills/dotnet-aot-compat/SKILL.md +265 -0
- package/package/corpus/skills/dotnet-aot-compat/references/polyfills.md +43 -0
- package/package/corpus/skills/dotnet-trace-collect/LICENSE +23 -0
- package/package/corpus/skills/dotnet-trace-collect/NOTICE +65 -0
- package/package/corpus/skills/dotnet-trace-collect/SKILL.md +116 -0
- package/package/corpus/skills/dotnet-trace-collect/references/dotnet-monitor.md +97 -0
- package/package/corpus/skills/dotnet-trace-collect/references/dotnet-trace-collect-linux.md +84 -0
- package/package/corpus/skills/dotnet-trace-collect/references/dotnet-trace-collect.md +91 -0
- package/package/corpus/skills/dotnet-trace-collect/references/perfcollect.md +200 -0
- package/package/corpus/skills/dotnet-trace-collect/references/perfview.md +141 -0
- package/package/corpus/skills/dotnet-trace-collect/references/tool-selection.md +160 -0
- package/package/corpus/skills/dump-collect/LICENSE +23 -0
- package/package/corpus/skills/dump-collect/NOTICE +40 -0
- package/package/corpus/skills/dump-collect/SKILL.md +97 -0
- package/package/corpus/skills/dump-collect/references/container-dumps.md +412 -0
- package/package/corpus/skills/dump-collect/references/coreclr-dumps.md +108 -0
- package/package/corpus/skills/dump-collect/references/nativeaot-dumps.md +280 -0
- package/package/corpus/skills/eliminate-redundant-restores/LICENSE +23 -0
- package/package/corpus/skills/eliminate-redundant-restores/NOTICE +19 -0
- package/package/corpus/skills/eliminate-redundant-restores/SKILL.md +112 -0
- package/package/corpus/skills/extension-points/LICENSE +23 -0
- package/package/corpus/skills/extension-points/NOTICE +18 -0
- package/package/corpus/skills/extension-points/SKILL.md +248 -0
- package/package/corpus/skills/filter-syntax/LICENSE +23 -0
- package/package/corpus/skills/filter-syntax/NOTICE +17 -0
- package/package/corpus/skills/filter-syntax/SKILL.md +179 -0
- package/package/corpus/skills/including-generated-files/LICENSE +23 -0
- package/package/corpus/skills/including-generated-files/NOTICE +17 -0
- package/package/corpus/skills/including-generated-files/SKILL.md +139 -0
- package/package/corpus/skills/microbenchmarking/LICENSE +23 -0
- package/package/corpus/skills/microbenchmarking/NOTICE +36 -0
- package/package/corpus/skills/microbenchmarking/SKILL.md +131 -0
- package/package/corpus/skills/microbenchmarking/references/bdn-internals-and-tuning.md +143 -0
- package/package/corpus/skills/microbenchmarking/references/comparison-strategies.md +200 -0
- package/package/corpus/skills/microbenchmarking/references/diagnosers-and-exporters.md +150 -0
- package/package/corpus/skills/microbenchmarking/references/project-setup-and-running.md +189 -0
- package/package/corpus/skills/microbenchmarking/references/writing-benchmarks.md +310 -0
- package/package/corpus/skills/msbuild/LICENSE +23 -0
- package/package/corpus/skills/msbuild/NOTICE +55 -0
- package/package/corpus/skills/msbuild/SKILL.md +117 -0
- package/package/corpus/skills/msbuild/references/antipatterns/additional-antipatterns.md +291 -0
- package/package/corpus/skills/msbuild/references/antipatterns/incremental-build-inputs-outputs.md +57 -0
- package/package/corpus/skills/msbuild/references/antipatterns/private-assets.md +29 -0
- package/package/corpus/skills/msbuild/references/binlog-failure-analysis.md +85 -0
- package/package/corpus/skills/msbuild/references/binlog-generation.md +69 -0
- package/package/corpus/skills/msbuild/references/build-perf-baseline.md +493 -0
- package/package/corpus/skills/msbuild/references/build-perf-diagnostics.md +268 -0
- package/package/corpus/skills/msbuild/references/copy-to-output-directory.md +183 -0
- package/package/corpus/skills/msbuild/references/eval-performance.md +190 -0
- package/package/corpus/skills/msbuild/references/extension-points.md +281 -0
- package/package/corpus/skills/msbuild/references/incremental-build.md +365 -0
- package/package/corpus/skills/msbuild/references/msbuild-antipatterns.md +401 -0
- package/package/corpus/skills/orleans/LICENSE +21 -0
- package/package/corpus/skills/orleans/NOTICE +64 -0
- package/package/corpus/skills/orleans/SKILL.md +140 -0
- package/package/corpus/skills/orleans/references/anti-patterns.md +660 -0
- package/package/corpus/skills/orleans/references/configuration-api.md +933 -0
- package/package/corpus/skills/orleans/references/examples.md +66 -0
- package/package/corpus/skills/orleans/references/grain-api.md +784 -0
- package/package/corpus/skills/orleans/references/grains.md +75 -0
- package/package/corpus/skills/orleans/references/hosting.md +71 -0
- package/package/corpus/skills/orleans/references/implementation.md +591 -0
- package/package/corpus/skills/orleans/references/official-docs-index.md +250 -0
- package/package/corpus/skills/orleans/references/patterns.md +811 -0
- package/package/corpus/skills/orleans/references/persistence-api.md +495 -0
- package/package/corpus/skills/orleans/references/primitive-selection.md +35 -0
- package/package/corpus/skills/orleans/references/scheduling-and-services.md +295 -0
- package/package/corpus/skills/orleans/references/serialization-api.md +299 -0
- package/package/corpus/skills/orleans/references/streaming-api.md +396 -0
- package/package/corpus/skills/orleans/references/testing-patterns.md +210 -0
- package/package/corpus/skills/platform-detection/LICENSE +23 -0
- package/package/corpus/skills/platform-detection/NOTICE +17 -0
- package/package/corpus/skills/platform-detection/SKILL.md +230 -0
- package/package/corpus/skills/platform-detection/references/command-mode.md +27 -0
- package/package/corpus/skills/profiling/LICENSE +21 -0
- package/package/corpus/skills/profiling/NOTICE +50 -0
- package/package/corpus/skills/profiling/SKILL.md +144 -0
- package/package/corpus/skills/profiling/references/commands.md +439 -0
- package/package/corpus/skills/profiling/references/patterns.md +586 -0
- package/package/corpus/skills/profiling/references/profiling.md +190 -0
- package/package/corpus/skills/run-tests/LICENSE +23 -0
- package/package/corpus/skills/run-tests/NOTICE +19 -0
- package/package/corpus/skills/run-tests/SKILL.md +305 -0
- package/package/corpus/skills/target-authoring/LICENSE +23 -0
- package/package/corpus/skills/target-authoring/NOTICE +17 -0
- package/package/corpus/skills/target-authoring/SKILL.md +165 -0
- package/package/corpus/skills/vectorization/LICENSE +23 -0
- package/package/corpus/skills/vectorization/NOTICE +17 -0
- package/package/corpus/skills/vectorization/SKILL.md +189 -0
- package/package/profile-catalog.json +87 -1
- package/package.json +3 -2
package/package/corpus/skills/cratis-ci-pipeline-performance/references/build-once-and-shard.md
ADDED
|
@@ -0,0 +1,158 @@
|
|
|
1
|
+
# Build once, shard the tests
|
|
2
|
+
|
|
3
|
+
## Contents
|
|
4
|
+
|
|
5
|
+
- When it fits
|
|
6
|
+
- Shape
|
|
7
|
+
- Decisions
|
|
8
|
+
- Pitfalls
|
|
9
|
+
- Fewer, bigger jobs
|
|
10
|
+
|
|
11
|
+
Use this when one workflow builds the same code more than once, or runs build and
|
|
12
|
+
tests serially in one long job. The pattern: a single build job compiles in
|
|
13
|
+
`Release` and uploads the output; a shard matrix downloads it and runs the tests
|
|
14
|
+
with `--no-build`. The wall clock becomes build + the slowest shard instead of
|
|
15
|
+
build + all tests.
|
|
16
|
+
|
|
17
|
+
## When it fits
|
|
18
|
+
|
|
19
|
+
- The report shows `execution-bound` and one job holds most of the span.
|
|
20
|
+
- Several jobs each restore and compile the same solution.
|
|
21
|
+
- Tests are independent by project or namespace.
|
|
22
|
+
|
|
23
|
+
It does not fit when the build is short compared with job start cost, or when
|
|
24
|
+
the report is `queue-bound`: more shards are more independently scheduled jobs.
|
|
25
|
+
Merge shards first, then re-measure.
|
|
26
|
+
|
|
27
|
+
## Shape
|
|
28
|
+
|
|
29
|
+
```yaml
|
|
30
|
+
jobs:
|
|
31
|
+
build:
|
|
32
|
+
runs-on: ubuntu-latest
|
|
33
|
+
timeout-minutes: 45
|
|
34
|
+
permissions:
|
|
35
|
+
contents: read
|
|
36
|
+
steps:
|
|
37
|
+
- uses: actions/checkout@v7 # pin by full commit SHA
|
|
38
|
+
- uses: actions/setup-dotnet@v6 # pin by full commit SHA
|
|
39
|
+
- uses: Cratis/Workflows/.github/actions/dotnet-cache@main
|
|
40
|
+
- run: dotnet build Repo.slnx --configuration Release
|
|
41
|
+
- name: Archive build output
|
|
42
|
+
run: |
|
|
43
|
+
set -euo pipefail
|
|
44
|
+
find . -type d -name bin -not -path '*/node_modules/*' -not -path './.git/*' -print0 \
|
|
45
|
+
| tar --null -T - --zstd -cf build-output.tar.zst
|
|
46
|
+
- uses: actions/upload-artifact@v7 # pin by full commit SHA
|
|
47
|
+
with:
|
|
48
|
+
name: build-output
|
|
49
|
+
path: build-output.tar.zst
|
|
50
|
+
compression-level: 0
|
|
51
|
+
retention-days: 1
|
|
52
|
+
if-no-files-found: error
|
|
53
|
+
|
|
54
|
+
specs:
|
|
55
|
+
name: ${{ matrix.shard }}
|
|
56
|
+
needs: [build]
|
|
57
|
+
runs-on: ubuntu-latest
|
|
58
|
+
timeout-minutes: 30
|
|
59
|
+
permissions:
|
|
60
|
+
contents: read
|
|
61
|
+
strategy:
|
|
62
|
+
fail-fast: false
|
|
63
|
+
matrix:
|
|
64
|
+
include:
|
|
65
|
+
- shard: specs-1
|
|
66
|
+
projects: Source/Core.Specs Source/Storage.Specs
|
|
67
|
+
- shard: specs-2
|
|
68
|
+
projects: Source/Api.Specs
|
|
69
|
+
- shard: specs-3
|
|
70
|
+
projects: Source/Client.Specs Source/Tools.Specs
|
|
71
|
+
steps:
|
|
72
|
+
- uses: actions/checkout@v7 # pin by full commit SHA
|
|
73
|
+
- uses: actions/setup-dotnet@v6 # pin by full commit SHA
|
|
74
|
+
- uses: actions/download-artifact@v8 # pin by full commit SHA
|
|
75
|
+
with:
|
|
76
|
+
name: build-output
|
|
77
|
+
- run: tar --zstd -xf build-output.tar.zst
|
|
78
|
+
- name: Test the shard's projects
|
|
79
|
+
env:
|
|
80
|
+
PROJECTS: ${{ matrix.projects }}
|
|
81
|
+
run: |
|
|
82
|
+
failed=0
|
|
83
|
+
for project in $PROJECTS; do
|
|
84
|
+
dotnet test "$project" --no-build --configuration Release \
|
|
85
|
+
--logger "trx;LogFileName=$(basename "$project").trx" \
|
|
86
|
+
--results-directory "$RUNNER_TEMP/test-results" || failed=1
|
|
87
|
+
done
|
|
88
|
+
exit "$failed"
|
|
89
|
+
- name: Verify every project ran tests
|
|
90
|
+
if: ${{ !cancelled() }}
|
|
91
|
+
env:
|
|
92
|
+
PROJECTS: ${{ matrix.projects }}
|
|
93
|
+
run: |
|
|
94
|
+
failed=0
|
|
95
|
+
for project in $PROJECTS; do
|
|
96
|
+
file="$RUNNER_TEMP/test-results/$(basename "$project").trx"
|
|
97
|
+
executed=$(grep -o -m1 'executed="[0-9]*"' "$file" 2>/dev/null | grep -o '[0-9]*' || echo 0)
|
|
98
|
+
echo "$project executed $executed tests"
|
|
99
|
+
[ "$executed" -gt 0 ] || failed=1
|
|
100
|
+
done
|
|
101
|
+
exit "$failed"
|
|
102
|
+
- uses: actions/upload-artifact@v7 # pin by full commit SHA
|
|
103
|
+
if: ${{ !cancelled() }}
|
|
104
|
+
with:
|
|
105
|
+
name: test-results-${{ matrix.shard }}
|
|
106
|
+
path: ${{ runner.temp }}/test-results/*.trx
|
|
107
|
+
if-no-files-found: warn
|
|
108
|
+
```
|
|
109
|
+
|
|
110
|
+
Each matrix entry carries its own `projects` list, which the test step reads, so
|
|
111
|
+
shards run different tests. For a test framework filter instead of a project list,
|
|
112
|
+
carry the filter in the entry (`filter: FullyQualifiedName~Core`) and pass it as
|
|
113
|
+
`--filter`. Keep the assignment in the workflow or one checked-in script so that
|
|
114
|
+
adding a project cannot leave it unassigned; review the list when a project is added.
|
|
115
|
+
|
|
116
|
+
To balance, read each shard job's median duration from the slowest-jobs table of
|
|
117
|
+
`ci-timings.sh`, move a project from the longest shard to the shortest, and
|
|
118
|
+
re-measure; stop when the spread is within the job start cost.
|
|
119
|
+
|
|
120
|
+
## Decisions
|
|
121
|
+
|
|
122
|
+
- **Artifact, not cache, between jobs of one run.** A cache keyed on the commit
|
|
123
|
+
SHA is immutable: one failed save poisons the key for every later attempt and a
|
|
124
|
+
rerun cannot recover. An artifact belongs to the run and is the intended
|
|
125
|
+
primitive for passing build output between its jobs.
|
|
126
|
+
- **Archive with `tar`.** Artifact upload does not preserve file permissions or
|
|
127
|
+
empty directories; a tar keeps both. Output that was already compressed should
|
|
128
|
+
not be compressed again: `compression-level: 0`.
|
|
129
|
+
- **Include every consumed output.** Frontend-dependent specs need the built
|
|
130
|
+
`dist` folders as well as `bin`; list them in the archive.
|
|
131
|
+
- **Short retention.** `retention-days: 1` for build output nobody downloads by
|
|
132
|
+
hand. Test results can live longer.
|
|
133
|
+
- **Same configuration end to end.** Test with the configuration that built
|
|
134
|
+
(`--configuration Release --no-build`); a different one silently rebuilds.
|
|
135
|
+
- **`fail-fast: false`** on shard matrices, so one failing shard does not hide the
|
|
136
|
+
results of the others, and a rerun of failed jobs reruns only the failed shard.
|
|
137
|
+
- **Upload results with `if: ${{ !cancelled() }}`** so failures keep their TRX.
|
|
138
|
+
|
|
139
|
+
## Pitfalls
|
|
140
|
+
|
|
141
|
+
- A filter or project list that matches nothing passes `dotnet test`. End every
|
|
142
|
+
shard with a check that each assigned project or namespace ran at least one test,
|
|
143
|
+
and print the counts. Zero tests is a failed shard.
|
|
144
|
+
- Shards balance by test time, not by project count (see the balancing steps
|
|
145
|
+
after the example).
|
|
146
|
+
- A shard that downloads a large artifact pays that transfer on every job; measure
|
|
147
|
+
the download step in the slowest-steps table before adding shards.
|
|
148
|
+
- Static assets outside `bin`/`dist` (generated sources, local tool caches) must
|
|
149
|
+
be in the archive or regenerated by the shard.
|
|
150
|
+
|
|
151
|
+
## Fewer, bigger jobs
|
|
152
|
+
|
|
153
|
+
When the report is `queue-bound`, every extra job costs a scheduler start, a
|
|
154
|
+
checkout and a toolchain setup. Merge sibling jobs that share setup (several
|
|
155
|
+
lint or format checks, several small build legs) into one job with several steps.
|
|
156
|
+
Keep a job separate only when it is on the critical path and long enough that
|
|
157
|
+
parallelism repays its start. State the expected effect on both wall clock and
|
|
158
|
+
runner-minutes, since merging parallel work lengthens the chain.
|
|
@@ -0,0 +1,86 @@
|
|
|
1
|
+
# Caching
|
|
2
|
+
|
|
3
|
+
Use this when a job spends measurable time restoring packages, installing a
|
|
4
|
+
frontend toolchain or rebuilding Docker layers. Check the slowest-steps table
|
|
5
|
+
first: a step under a minute is not worth a cache.
|
|
6
|
+
|
|
7
|
+
## Contents
|
|
8
|
+
|
|
9
|
+
- Shared Cratis cache actions
|
|
10
|
+
- NuGet and Yarn determinism
|
|
11
|
+
- Docker layer cache
|
|
12
|
+
- Scope, eviction and safety
|
|
13
|
+
|
|
14
|
+
## Shared Cratis cache actions
|
|
15
|
+
|
|
16
|
+
The `Cratis/Workflows` repository ships two composite actions. Use them instead
|
|
17
|
+
of hand-written `actions/cache` steps so every workflow of a repository uses one
|
|
18
|
+
key and one path list.
|
|
19
|
+
|
|
20
|
+
```yaml
|
|
21
|
+
- uses: Cratis/Workflows/.github/actions/dotnet-cache@main
|
|
22
|
+
- uses: Cratis/Workflows/.github/actions/yarn-cache@main
|
|
23
|
+
with:
|
|
24
|
+
working-directory: Source/JavaScript
|
|
25
|
+
```
|
|
26
|
+
|
|
27
|
+
- `mode` is `auto` by default: pull requests only restore; a push to the default
|
|
28
|
+
branch, a schedule or a manual run on the default branch saves. Use `restore`
|
|
29
|
+
to never save and `save` to always save.
|
|
30
|
+
- `dotnet-cache` inputs: `path`, `key-files`, `key-prefix`, `mode`. Keep `path`
|
|
31
|
+
identical everywhere; a different path list is a different cache. Bump the
|
|
32
|
+
version suffix of `key-prefix` to start a fresh cache.
|
|
33
|
+
- Outputs: `cache-hit`, `matched-key`, `key`, `mode`.
|
|
34
|
+
- A cache is only saved from the default branch, so a repository with no push
|
|
35
|
+
workflow on that branch never warms one. Add a scheduled warm-up workflow that
|
|
36
|
+
runs the same restore and build.
|
|
37
|
+
|
|
38
|
+
## NuGet and Yarn determinism
|
|
39
|
+
|
|
40
|
+
A cache is only as stable as its key. Make the key inputs deterministic.
|
|
41
|
+
|
|
42
|
+
- NuGet: enable lock files (`<RestorePackagesWithLockFile>true</RestorePackagesWithLockFile>`)
|
|
43
|
+
and restore with `dotnet restore --locked-mode` in CI, so the lock file hash
|
|
44
|
+
keys the cache and a drifting dependency fails loudly.
|
|
45
|
+
- Yarn: commit `yarn.lock` and install with `yarn install --immutable`; the
|
|
46
|
+
install fails instead of rewriting the lock file.
|
|
47
|
+
- Cache the package download directories, not `bin`/`obj` output. Build output
|
|
48
|
+
caches across commits are rarely correct; share build output within a run as an
|
|
49
|
+
artifact instead (see `build-once-and-shard.md`).
|
|
50
|
+
|
|
51
|
+
## Docker layer cache
|
|
52
|
+
|
|
53
|
+
```yaml
|
|
54
|
+
- uses: docker/setup-buildx-action@v4 # pin by full commit SHA
|
|
55
|
+
- uses: docker/build-push-action@v7 # pin by full commit SHA
|
|
56
|
+
with:
|
|
57
|
+
context: .
|
|
58
|
+
push: false
|
|
59
|
+
cache-from: type=gha,scope=api-linux-amd64
|
|
60
|
+
cache-to: type=gha,scope=api-linux-amd64,mode=max
|
|
61
|
+
```
|
|
62
|
+
|
|
63
|
+
- Set a custom `scope` per image and platform. The default scope is shared, so
|
|
64
|
+
two builds overwrite each other's cache and both see misses.
|
|
65
|
+
- `mode=max` caches intermediate layers of multi-stage builds; the default caches
|
|
66
|
+
only the final image layers.
|
|
67
|
+
- Order the Dockerfile so rarely changing layers (base image, dependency
|
|
68
|
+
restore) come before source copies.
|
|
69
|
+
- When an image is built once and pulled by several jobs, pushing it to a
|
|
70
|
+
registry and pulling it costs a transfer per job; measure that pull in the
|
|
71
|
+
slowest-steps table against rebuilding from cache.
|
|
72
|
+
|
|
73
|
+
## Scope, eviction and safety
|
|
74
|
+
|
|
75
|
+
- A pull request run can read caches of its own ref and of the base branch; a
|
|
76
|
+
cache saved on the default branch is readable by every branch. A cache saved by
|
|
77
|
+
one pull request is not visible to others.
|
|
78
|
+
- Repository cache storage is bounded (10 GB by default). When full, or after a
|
|
79
|
+
week without access, entries are evicted, least recently used first. A large
|
|
80
|
+
rarely used cache can evict a hot one.
|
|
81
|
+
- Caches must not contain secrets, tokens or credentials: restored content is
|
|
82
|
+
executed and readable by later runs of the same scope.
|
|
83
|
+
- Never restore a cache written by an untrusted pull request into a privileged
|
|
84
|
+
workflow. See `security-and-pinning.md`.
|
|
85
|
+
- Verify a cache works by reading the `cache-hit` output (or the restore step
|
|
86
|
+
log) on a second run, then re-measure with `ci-timings.sh`.
|
package/package/corpus/skills/cratis-ci-pipeline-performance/references/flaky-and-hung-jobs.md
ADDED
|
@@ -0,0 +1,70 @@
|
|
|
1
|
+
# Flaky and hung jobs
|
|
2
|
+
|
|
3
|
+
Use this when the report is `failure-bound` (failed or rerun at or above the
|
|
4
|
+
hint threshold) or a job occasionally runs to its timeout. Reruns and failures
|
|
5
|
+
cost runner-minutes and developer trust; diagnose before retrying.
|
|
6
|
+
|
|
7
|
+
## Diagnose
|
|
8
|
+
|
|
9
|
+
1. Run `ci-timings.sh` with `--branch main` and again with `--event pull_request`.
|
|
10
|
+
Failures on the default branch with no code change are flakes or environment
|
|
11
|
+
problems; failures only on pull requests are mostly real.
|
|
12
|
+
2. Group the failing runs by job and step name. One step that fails across many
|
|
13
|
+
runs is a defect in that step, not noise.
|
|
14
|
+
3. Separate the cause: a hung test, a timeout from an external service, an
|
|
15
|
+
out-of-memory kill, or an infrastructure cancellation.
|
|
16
|
+
|
|
17
|
+
## Bound every job
|
|
18
|
+
|
|
19
|
+
- `timeout-minutes` on every job, per the rule. A hung job otherwise holds a
|
|
20
|
+
runner slot for up to six hours.
|
|
21
|
+
- Set the job timeout above the test timeout, so dump collection and cleanup have
|
|
22
|
+
time to finish.
|
|
23
|
+
|
|
24
|
+
## Find hangs and crashes in .NET
|
|
25
|
+
|
|
26
|
+
```bash
|
|
27
|
+
dotnet test Repo.slnx --configuration Release --no-build \
|
|
28
|
+
--blame-hang --blame-hang-timeout 5m --blame-crash \
|
|
29
|
+
--results-directory TestResults
|
|
30
|
+
```
|
|
31
|
+
|
|
32
|
+
- This is the VSTest form. Microsoft.Testing.Platform projects use their own
|
|
33
|
+
crash and hang dump extensions and options; do not mix the two sets of switches.
|
|
34
|
+
- Upload `TestResults` as an artifact with `if: ${{ !cancelled() }}`, so the dump
|
|
35
|
+
survives the failure.
|
|
36
|
+
|
|
37
|
+
## Retry only a diagnosed flake
|
|
38
|
+
|
|
39
|
+
- Retry the smallest unit: a single test or a single failed job, never the whole
|
|
40
|
+
suite. With Microsoft.Testing.Platform, the retry extension provides
|
|
41
|
+
`--retry-failed-tests` with a failure-percentage ceiling; plain VSTest has no
|
|
42
|
+
equivalent flag.
|
|
43
|
+
- Keep the first-attempt failure visible. A retry that turns red green without a
|
|
44
|
+
record hides the flake and doubles minutes.
|
|
45
|
+
- Retrying a deterministic failure only consumes capacity.
|
|
46
|
+
|
|
47
|
+
## Quarantine
|
|
48
|
+
|
|
49
|
+
A quarantined test is a decision with an owner, not a deletion:
|
|
50
|
+
|
|
51
|
+
- Mark it with the framework's skip or category mechanism and a reason that names
|
|
52
|
+
an issue.
|
|
53
|
+
- Run quarantined tests in a separate, visible, non-required lane so they keep
|
|
54
|
+
producing signal.
|
|
55
|
+
- Give the issue an owner and an expiry; an entry nobody revisits is a check
|
|
56
|
+
quietly deleted.
|
|
57
|
+
|
|
58
|
+
## Fail fast
|
|
59
|
+
|
|
60
|
+
Put the cheap, high-signal gates (format, lint, unit-level tests) before the
|
|
61
|
+
expensive legs and make the expensive legs `needs` them. A failure then costs
|
|
62
|
+
minutes instead of the full pipeline. Keep the gate short: a slow gate on the
|
|
63
|
+
critical path moves the cost instead of removing it.
|
|
64
|
+
|
|
65
|
+
## Cancelled work
|
|
66
|
+
|
|
67
|
+
A high cancelled share is expected on pull requests with `cancel-in-progress`.
|
|
68
|
+
It is waste only when the cancelled runs had already spent their expensive legs.
|
|
69
|
+
Reduce it by ordering cheap legs first and by keeping the full matrix off the
|
|
70
|
+
pull-request path, not by removing the cancellation.
|
|
@@ -0,0 +1,67 @@
|
|
|
1
|
+
# Publish and release pipelines
|
|
2
|
+
|
|
3
|
+
Use this when a release, package-publish or deploy workflow queues, loses runs,
|
|
4
|
+
or rebuilds what a pull-request run already built and tested. Release method and
|
|
5
|
+
assurance tiers belong to `cratis-governed-release-methodology`.
|
|
6
|
+
|
|
7
|
+
## Concurrency for publishing
|
|
8
|
+
|
|
9
|
+
```yaml
|
|
10
|
+
concurrency:
|
|
11
|
+
group: publish-${{ github.repository }}
|
|
12
|
+
cancel-in-progress: false
|
|
13
|
+
queue: max
|
|
14
|
+
```
|
|
15
|
+
|
|
16
|
+
- Pull-request verification cancels superseded runs. Publish never does: a
|
|
17
|
+
cancelled half-publish is worse than a queued one.
|
|
18
|
+
- A concurrency group holds one running and, by default, one pending run; a newer
|
|
19
|
+
arrival replaces the pending one. `queue: max` keeps up to 100 pending runs in
|
|
20
|
+
the group and processes them in order. It is valid only while
|
|
21
|
+
`cancel-in-progress` is `false` or unset; never combine it with `true`.
|
|
22
|
+
- Replacement only happens between runs of the same group, so read the group key.
|
|
23
|
+
Merges into the default branch share one group: a push, a repository-wide key, or
|
|
24
|
+
`github.ref` on a merged `pull_request` `closed` event, where `github.ref` is the
|
|
25
|
+
base branch (for example `refs/heads/main`). A newer merge's pending run then
|
|
26
|
+
replaces the older pending one unless `queue: max` is set. Only a `closed` event
|
|
27
|
+
of an unmerged pull request keeps `refs/pull/N/merge`, a group of its own.
|
|
28
|
+
- A replaced pending run is lost work only when that run alone would have released
|
|
29
|
+
something. Release tooling that aggregates everything since the last release
|
|
30
|
+
loses nothing when a pending run is replaced; such a workflow may keep one
|
|
31
|
+
pending slot, with a comment beside the `concurrency` block saying so.
|
|
32
|
+
- Groups are repository-scoped; this is not an organization-wide release lock.
|
|
33
|
+
- Check the symptom in the report: many cancelled publish runs under one group
|
|
34
|
+
point at a replaced pending slot.
|
|
35
|
+
|
|
36
|
+
## Publish from the tested artifacts
|
|
37
|
+
|
|
38
|
+
A publish job that rebuilds can ship something different from what was tested,
|
|
39
|
+
and pays the build twice.
|
|
40
|
+
|
|
41
|
+
- Build once per publish run (per architecture where a native build differs),
|
|
42
|
+
pack with `--no-build`, and publish exactly those packages and images.
|
|
43
|
+
- Share build output between jobs of the run as an artifact (see
|
|
44
|
+
`build-once-and-shard.md`). Do not pull a pull-request run's artifact into a
|
|
45
|
+
release: artifacts expire and belong to another commit.
|
|
46
|
+
- Build the way consumers' strictest CI builds (`Release`, analyzers on); a
|
|
47
|
+
`--no-build` pack of a `Debug` build ships the wrong thing.
|
|
48
|
+
- When several services publish images from one build, build the image once,
|
|
49
|
+
push it, and have the per-service jobs reuse it; compare the per-job pull time
|
|
50
|
+
against rebuilding from the Docker cache.
|
|
51
|
+
- Keep a version resolved once in an early job and passed down as an output, so
|
|
52
|
+
every job in the run packs the same version.
|
|
53
|
+
|
|
54
|
+
## Timeouts and permissions
|
|
55
|
+
|
|
56
|
+
- Publish and release jobs use a 60-minute `timeout-minutes` tier, per the rule.
|
|
57
|
+
- Grant `contents: write` or `packages: write` on the job that publishes, not on
|
|
58
|
+
the workflow, and keep build jobs on `contents: read`.
|
|
59
|
+
- Prefer OIDC trusted publishing to a long-lived registry API key where the
|
|
60
|
+
registry supports it. See `security-and-pinning.md`.
|
|
61
|
+
|
|
62
|
+
## Speed without weakening the release
|
|
63
|
+
|
|
64
|
+
- Do not skip validation on the release path to save minutes; move other work off
|
|
65
|
+
it instead (docs jobs, matrices already covered by schedule).
|
|
66
|
+
- Canary before promotion and recovery disposition are release-method questions;
|
|
67
|
+
hand them to `cratis-governed-release-methodology`.
|
|
@@ -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.
|