@cratis/pi 7.101.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.
Files changed (30) hide show
  1. package/package/corpus/harnesses/copilot/instructions/github-actions.instructions.md +17 -0
  2. package/package/corpus/rules/github-actions.md +17 -0
  3. package/package/corpus/skills/cratis-ci-pipeline-performance/LICENSE +2 -0
  4. package/package/corpus/skills/cratis-ci-pipeline-performance/SKILL.md +201 -0
  5. package/package/corpus/skills/cratis-ci-pipeline-performance/references/build-once-and-shard.md +158 -0
  6. package/package/corpus/skills/cratis-ci-pipeline-performance/references/caching.md +86 -0
  7. package/package/corpus/skills/cratis-ci-pipeline-performance/references/flaky-and-hung-jobs.md +70 -0
  8. package/package/corpus/skills/cratis-ci-pipeline-performance/references/publish-pipelines.md +67 -0
  9. package/package/corpus/skills/cratis-ci-pipeline-performance/references/required-checks-and-path-filters.md +117 -0
  10. package/package/corpus/skills/cratis-ci-pipeline-performance/references/runner-sizing-and-images.md +65 -0
  11. package/package/corpus/skills/cratis-ci-pipeline-performance/references/security-and-pinning.md +73 -0
  12. package/package/corpus/skills/cratis-ci-pipeline-performance/references/sources.md +33 -0
  13. package/package/corpus/skills/cratis-ci-pipeline-performance/references/test-setup-cost.md +82 -0
  14. package/package/corpus/skills/cratis-ci-pipeline-performance/scripts/ci-timings.sh +356 -0
  15. package/package/corpus/skills/cratis-ci-pipeline-performance/scripts/fixtures/jobs/1003.json +79 -0
  16. package/package/corpus/skills/cratis-ci-pipeline-performance/scripts/fixtures/jobs/1007.json +79 -0
  17. package/package/corpus/skills/cratis-ci-pipeline-performance/scripts/fixtures/jobs/1010.json +79 -0
  18. package/package/corpus/skills/cratis-ci-pipeline-performance/scripts/fixtures/runs.json +102 -0
  19. package/package/corpus/skills/cratis-engineering-local-cross-repo-verification/LICENSE +2 -0
  20. package/package/corpus/skills/cratis-engineering-local-cross-repo-verification/SKILL.md +244 -0
  21. package/package/corpus/skills/cratis-engineering-local-cross-repo-verification/references/docker-images.md +86 -0
  22. package/package/corpus/skills/cratis-engineering-local-cross-repo-verification/references/dotnet-nuget.md +87 -0
  23. package/package/corpus/skills/cratis-engineering-local-cross-repo-verification/references/go-jvm-python-elixir.md +83 -0
  24. package/package/corpus/skills/cratis-engineering-local-cross-repo-verification/references/npm-yarn.md +78 -0
  25. package/package/corpus/skills/cratis-engineering-local-cross-repo-verification/references/other-environments.md +60 -0
  26. package/package/corpus/skills/cratis-engineering-local-cross-repo-verification/references/worked-examples.md +97 -0
  27. package/package/corpus/skills/cratis-engineering-local-cross-repo-verification/scripts/cratis-local-packages.sh +778 -0
  28. package/package/corpus/skills/cratis-engineering-local-cross-repo-verification/scripts/npm-restore-check.cjs +109 -0
  29. package/package/profile-catalog.json +2 -0
  30. package/package.json +3 -2
@@ -0,0 +1,109 @@
1
+ // Copyright (c) Cratis. All rights reserved.
2
+ // Licensed under the MIT license. See LICENSE file in the project root for full license information.
3
+ 'use strict';
4
+
5
+ // Read-only guard: all validation completes before the shell restores either file.
6
+ const fs = require('fs');
7
+ const path = require('path');
8
+ const {isDeepStrictEqual: equal} = require('util');
9
+ const [repo, backup] = process.argv.slice(2);
10
+ const read = (base, name) => fs.readFileSync(path.join(base, name), 'utf8');
11
+ const json = (base, name) => JSON.parse(read(base, name));
12
+ const refuse = message => { throw new Error(message); };
13
+
14
+ function overlayResolutions(original, expected) {
15
+ const before = original.resolutions || {};
16
+ const after = expected.resolutions || {};
17
+ return new Map(Object.entries(after).filter(([name, value]) => !equal(value, before[name])));
18
+ }
19
+
20
+ function checkManifest(original, current, overlay) {
21
+ for (const [name, value] of overlay) {
22
+ const actual = (current.resolutions || {})[name];
23
+ if (actual !== value && !equal(actual, (original.resolutions || {})[name]))
24
+ refuse('Changed overlay resolution: ' + name);
25
+ }
26
+ const withoutOverlay = manifest => {
27
+ const copy = JSON.parse(JSON.stringify(manifest));
28
+ if (copy.resolutions) {
29
+ for (const name of overlay.keys()) delete copy.resolutions[name];
30
+ if (!Object.keys(copy.resolutions).length) delete copy.resolutions;
31
+ }
32
+ return copy;
33
+ };
34
+ if (!equal(withoutOverlay(current), withoutOverlay(original)))
35
+ refuse('Unrelated package.json changes');
36
+ }
37
+
38
+ // Yarn 4 lockfiles have quoted top-level descriptor keys and indented bodies.
39
+ // Preserve bytes for every unaffected block, including metadata and comments;
40
+ // unsupported syntax or install churn is a conflict, not permission to overwrite.
41
+ function lockBlocks(text) {
42
+ const starts = [...text.matchAll(/^[^\s#].*$/gm)];
43
+ const header = text.slice(0, starts.length ? starts[0].index : text.length);
44
+ const blocks = new Map();
45
+ for (let index = 0; index < starts.length; index++) {
46
+ const line = starts[index][0];
47
+ const key = line === '__metadata:' ? '__metadata' : JSON.parse(line.slice(0, -1));
48
+ if (!line.endsWith(':') || typeof key !== 'string' || blocks.has(key))
49
+ refuse('Unsupported or duplicate lockfile key');
50
+ blocks.set(key, text.slice(starts[index].index, starts[index + 1]?.index ?? text.length));
51
+ }
52
+ return {header, blocks};
53
+ }
54
+
55
+ function descriptorNames(key) {
56
+ return key.split(', ').map(descriptor => {
57
+ const match = descriptor.match(/^(@[^/]+\/[^@]+|[^@]+)@/);
58
+ if (!match) refuse('Unsupported lockfile descriptor: ' + descriptor);
59
+ return match[1];
60
+ });
61
+ }
62
+
63
+ function localBlock(key, body, overlay) {
64
+ if (key === '__metadata') return false;
65
+ const names = descriptorNames(key);
66
+ const name = names[0];
67
+ if (!overlay.has(name) || names.some(other => other !== name)) return false;
68
+ const match = body.match(/^ resolution: (.+)$/m);
69
+ if (!match) return false;
70
+ const resolution = JSON.parse(match[1]);
71
+ const prefix = name + '@' + overlay.get(name);
72
+ // Yarn may append archive/locator parameters, but not a different filename.
73
+ return [prefix, encodeURI(prefix)].some(value => resolution === value ||
74
+ resolution.startsWith(value + '::') || resolution.startsWith(value + '#'));
75
+ }
76
+
77
+ function checkLock(original, current, overlay) {
78
+ if (original === current) return;
79
+ const before = lockBlocks(original), after = lockBlocks(current);
80
+ if (before.header !== after.header) refuse('Changed lockfile header');
81
+ const localNames = new Set();
82
+ for (const [key, body] of after.blocks) {
83
+ if (before.blocks.get(key) === body) continue;
84
+ if (!localBlock(key, body, overlay)) refuse('Unrelated lockfile change: ' + key);
85
+ for (const name of descriptorNames(key)) localNames.add(name);
86
+ }
87
+ for (const [key, body] of before.blocks) {
88
+ if (after.blocks.get(key) === body) continue;
89
+ // Only entries for locally replaced package names may change or vanish.
90
+ // Changed entries were already checked against the exact tarball above.
91
+ if (key === '__metadata' ||
92
+ descriptorNames(key).some(name => !localNames.has(name)))
93
+ refuse('Changed or removed unrelated lockfile entry: ' + key);
94
+ }
95
+ }
96
+
97
+ try {
98
+ const original = json(backup, 'package.json');
99
+ const expected = json(backup, 'expected-package.json');
100
+ const overlay = overlayResolutions(original, expected);
101
+ checkManifest(original, json(repo, 'package.json'), overlay);
102
+ const beforeExists = fs.existsSync(path.join(backup, 'yarn.lock'));
103
+ const afterExists = fs.existsSync(path.join(repo, 'yarn.lock'));
104
+ if (beforeExists !== afterExists) refuse('Lockfile created or removed; manual review required');
105
+ if (beforeExists) checkLock(read(backup, 'yarn.lock'), read(repo, 'yarn.lock'), overlay);
106
+ } catch (error) {
107
+ console.error('ERROR: ' + error.message);
108
+ process.exitCode = 2;
109
+ }
@@ -990,11 +990,13 @@
990
990
  {
991
991
  "id": "cratis/engineering/core",
992
992
  "availableTargets": [
993
+ "cratis-ci-pipeline-performance",
993
994
  "cratis-documentation-writing",
994
995
  "cratis-engineering-consolidation-guard",
995
996
  "cratis-engineering-decision-record",
996
997
  "cratis-engineering-docs-authoring",
997
998
  "cratis-engineering-effect-boundaries",
999
+ "cratis-engineering-local-cross-repo-verification",
998
1000
  "cratis-engineering-regression-cluster",
999
1001
  "cratis-release-notes",
1000
1002
  "cratis-technical-examples"
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@cratis/pi",
3
- "version": "7.101.0",
3
+ "version": "7.102.0",
4
4
  "description": "Configuration-aware Cratis AI integration for Pi",
5
5
  "type": "module",
6
6
  "files": [
@@ -20,7 +20,8 @@
20
20
  "ai",
21
21
  "skills"
22
22
  ],
23
- "homepage": "https://github.com/Cratis/AI",
23
+ "homepage": "https://www.cratis.io/ai/",
24
+ "author": "Cratis",
24
25
  "repository": {
25
26
  "type": "git",
26
27
  "url": "https://github.com/Cratis/AI",