@ship.zone/ci-spec 2.1.0 → 2.1.2
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/changelog.md +32 -0
- package/conformance/compile-cases.json +1464 -56
- package/conformance/runner-job-cases.json +11 -0
- package/conformance/runner-jobs/incoherent/command-argument-above-limit.json +70 -0
- package/dist_ts/00_commitinfo_data.js +1 -1
- package/package.json +1 -1
- package/readme.md +1 -1
- package/spec/ci-actions.md +25 -14
- package/spec/runner-protocol.md +2 -1
- package/spec/runner.openapi.json +1 -1
- package/ts/00_commitinfo_data.ts +1 -1
|
@@ -306,6 +306,17 @@
|
|
|
306
306
|
"code": "job_limit_incoherent"
|
|
307
307
|
}
|
|
308
308
|
},
|
|
309
|
+
{
|
|
310
|
+
"id": "fixture-incoherent-command-argument-above-limit",
|
|
311
|
+
"description": "An argument of 8,193 characters and 16,385 UTF-8 bytes: within the schema, above the argument limit.",
|
|
312
|
+
"job": {
|
|
313
|
+
"fixture": "conformance/runner-jobs/incoherent/command-argument-above-limit.json"
|
|
314
|
+
},
|
|
315
|
+
"expected": {
|
|
316
|
+
"outcome": "incoherent",
|
|
317
|
+
"code": "job_command_limit_exceeded"
|
|
318
|
+
}
|
|
319
|
+
},
|
|
309
320
|
{
|
|
310
321
|
"id": "fixture-incoherent-environment-above-limit",
|
|
311
322
|
"description": "128 step environment names and NPM_CONFIG_USERCONFIG of npmRead: 129 names.",
|
|
@@ -0,0 +1,70 @@
|
|
|
1
|
+
{
|
|
2
|
+
"spec": "2.0.0",
|
|
3
|
+
"sourceObjectId": "aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa",
|
|
4
|
+
"workflowDigest": "bbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbb",
|
|
5
|
+
"compiledPlanDigest": "71be63b5e4ddbe4aef1e4519f9c37756807a5e49b9071514050793d4945021bd",
|
|
6
|
+
"requirements": {
|
|
7
|
+
"executionProfile": "oci",
|
|
8
|
+
"isolation": "container",
|
|
9
|
+
"labels": ["linux"],
|
|
10
|
+
"features": ["source.tar-gz"],
|
|
11
|
+
"limits": {
|
|
12
|
+
"maximumSourceArchiveBytes": 1048576,
|
|
13
|
+
"maximumSourceExtractedBytes": 4194304,
|
|
14
|
+
"maximumSourceEntries": 1000,
|
|
15
|
+
"maximumArtifactArchiveBytes": 0,
|
|
16
|
+
"maximumArtifactExtractedBytes": 0,
|
|
17
|
+
"maximumArtifactEntries": 0,
|
|
18
|
+
"maximumArtifacts": 0,
|
|
19
|
+
"maximumCacheArchiveBytes": 0,
|
|
20
|
+
"maximumCacheExtractedBytes": 0,
|
|
21
|
+
"maximumCacheEntries": 0,
|
|
22
|
+
"maximumCaches": 0,
|
|
23
|
+
"maximumLogChunkBytes": 65536,
|
|
24
|
+
"maximumTotalLogBytes": 10485760,
|
|
25
|
+
"maximumWorkspaceBytes": 268435456,
|
|
26
|
+
"maximumWorkspaceEntries": 100000
|
|
27
|
+
}
|
|
28
|
+
},
|
|
29
|
+
"permissions": {
|
|
30
|
+
"source": "read",
|
|
31
|
+
"artifacts": "none",
|
|
32
|
+
"caches": "none",
|
|
33
|
+
"images": "none"
|
|
34
|
+
},
|
|
35
|
+
"network": {
|
|
36
|
+
"mode": "none"
|
|
37
|
+
},
|
|
38
|
+
"execution": {
|
|
39
|
+
"profile": "oci",
|
|
40
|
+
"image": "registry.example/ci@sha256:dddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddd",
|
|
41
|
+
"workingDirectory": "/workspace"
|
|
42
|
+
},
|
|
43
|
+
"steps": [
|
|
44
|
+
{
|
|
45
|
+
"name": "Test",
|
|
46
|
+
"command": ["pnpm", "ééééééééééééééééééééééééééééééééééééééééééééééééééééééééééééééééééééééééééééééééééééééééééééééééééééééééééééééééééééééééééééééééééééééééééééééééééééééééééééééééééééééééééééééééééééééééééééééééééééééééééééééééééééééééééééééééééééééééééééééééééééééééééééééééééééééééééééééééééééééééééééééééééééééééééééééééééééééééééééééééééééééééééééééééééééééééééééééééééééééééééééééééééééééééééééééééééééééééééééééééééééééééééééééééééééééééééééééééééééééééééééééééééééééééééééééééééééééééééééééééééééééééééééééééééééééééééééééééééééééééééééééééééééééééééééééééééééééééééééééééééééééééééééééééééééééééééééééééééééééééééééééééééééééééééééééééééééééééééééééééééééééééééééééééééééééééééééééééééééééééééééééééééééééééééééééééééééééééééééééééééééééééééééééééééééééééééééééééééééééééééééééééééééééééééééééééééééééééééééééééééééééééééééééééééééééééééééééééééééééééééééééééééééééééééééééééééééééééééééééééééééééééééééééééééééééééééééééééééééééééééééééééééééééééééééééééééééééééééééééééééééééééééééééééééééééééééééééééééééééééééééééééééééééééééééééééééééééééééééééééééééééééééééééééééééééééééééééééééééééééééééééééééééééééééééééééééééééééééééééééééééééééééééééééééééééééééééééééééééééééééééééééééééééééééééééééééééééééééééééééééééééééééééééééééééééééééééééééééééééééééééééééééééééééééééééééééééééééééééééééééééééééééééééééééééééééééééééééééééééééééééééééééééééééééééééééééééééééééééééééééééééééééééééééééééééééééééééééééééééééééééééééééééééééééééééééééééééééééééééééééééééééééééééééééééééééééééééééééééééééééééééééééééééééééééééééééééééééééééééééééééééééééééééééééééééééééééééééééééééééééééééééééééééééééééééééééééééééééééééééééééééééééééééééééééééééééééééééééééééééééééééééééééééééééééééééééééééééééééééééééééééééééééééééééééééééééééééééééééééééééééééééééééééééééééééééééééééééééééééééééééééééééééééééééééééééééééééééééééééééééééééééééééééééééééééééééééééééééééééééééééééééééééééééééééééééééééééééééééééééééééééééééééééééééééééééééééééééééééééééééééééééééééééééééééééééééééééééééééééééééééééééééééééééééééééééééééééééééééééééééééééééééééééééééééééééééééééééééééééééééééééééééééééééééééééééééééééééééééééééééééééééééééééééééééééééééééééééééééééééééééééééééééééééééééééééééééééééééééééééééééééééééééééééééééééééééééééééééééééééééééééééééééééééééééééééééééééééééééééééééééééééééééééééééééééééééééééééééééééééééééééééééééééééééééééééééééééééééééééééééééééééééééééééééééééééééééééééééééééééééééééééééééééééééééééééééééééééééééééééééééééééééééééééééééééééééééééééééééééééééééééééééééééééééééééééééééééééééééééééééééééééééééééééééééééééééééééééééééééééééééééééééééééééééééééééééééééééééééééééééééééééééééééééééééééééééééééééééééééééééééééééééééééééééééééééééééééééééééééééééééééééééééééééééééééééééééééééééééééééééééééééééééééééééééééééééééééééééééééééééééééééééééééééééééééééééééééééééééééééééééééééééééééééééééééééééééééééééééééééééééééééééééééééééééééééééééééééééééééééééééééééééééééééééééééééééééééééééééééééééééééééééééééééééééééééééééééééééééééééééééééééééééééééééééééééééééééééééééééééééééééééééééééééééééééééééééééééééééééééééééééééééééééééééééééééééééééééééééééééééééééééééééééééééééééééééééééééééééééééééééééééééééééééééééééééééééééééééééééééééééééééééééééééééééééééééééééééééééééééééééééééééééééééééééééééééééééééééééééééééééééééééééééééééééééééééééééééééééééééééééééééééééééééééééééééééééééééééééééééééééééééééééééééééééééééééééééééééééééééééééééééééééééééééééééééééééééééééééééééééééééééééééééééééééééééééééééééééééééééééééééééééééééééééééééééééééééééééééééééééééééééééééééééééééééééééééééééééééééééééééééééééééééééééééééééééééééééééééééééééééééééééééééééééééééééééééééééééééééééééééééééééééééééééééééééééééééééééééééééééééééééééééééééééééééééééééééééééééééééééééééééééééééééééééééééééééééééééééééééééééééééééééééééééééééééééééééééééééééééééééééééééééééééééééééééééééééééééééééééééééééééééééééééééééééééééééééééééééééééééééééééééééééééééééééééééééééééééééééééééééééééééééééééééééééééééééééééééééééééééééééééééééééééééééééééééééééééééééééééééééééééééééééééééééééééééééééééééééééééééééééééééééééééééééééééééééééééééééééééééééééééééééééééééééééééééééééééééééééééééééééééééééééééééééééééééééééééééééééééééééééééééééééééééééééééééééééééééééééééééééééééééééééééééééééééééééééééééééééééééééééééééééééééééééééééééééééééééééééééééééééééééééééééééééééééééééééééééééééééééééééééééééééééééééééééééééééééééééééééééééééééééééééééééééééééééééééééééééééééééééééééééééééééééééééééééééééééééééééééééééééééééééééééééééééééééééééééééééééééééééééééééééééééééééééééééééééééééééééééééééééééééééééééééééééééééééééééééééééééééééééééééééééééééééééééééééééééééééééééééééééééééééééééééééééééééééééééééééééééééééééééééééééééééééééééééééééééééééééééééééééééééééééééééééééééééééééééééééééééééééééééééééééééééééééééééééééééééééééééééééééééééééééééééééééééééééééééééééééééééééééééééééééééééééééééééééééééééééééééééééééééééééééééééééééééééééééééééééééééééééééééééééééééééééééééééééééééééééééééééééééééééééééééééééééééééééééééééééééééééééééééééééééééééééééééééééééééééééééééééééééééééééééééééééééééééééééééééééééééééééééééééééééééééééééééééééééééééééééééééééééééééééééééééééééééééééééééééééééééééééééééééééééééééééééééééééééééééééééééééééééééééééééééééééééééééééééééééééééééééééééééééééééééééééééééééééééééééééééééééééééééééééééééééééééééééééééééééééééééééééééééééééééééééééééééééééééééééééééééééééééééééééééééééééééééééééééééééééééééééééééééééééééééééééééééééééééééééééééééééééééééééééééééééééééééééééééééééééééééééééééééééééééééééééééééééééééééééééééééééééééééééééééééééééééééééééééééééééééééééééééééééééééééééééééééééééééééééééééééééééééééééééééééééééééééééééééééééééééééééééééééééééééééééééééééééééééééééééééééééééééééééééééééééééééééééééééééééééééééééééééééééééééééééééééééééééééééééééééééééééééééééééééééééééééééééééééééééééééééééééééééééééééééééééééééééééééééééééééééééééééééééééééééééééééééééééééééééééééééééééééééééééééééééééééééééééééééééééééééééééééééééééééééééééééééééééééééééééééééééééééééééééééééééééééééééééééééééééééééééééééééééééééééééééééééééééééééééééééééééééééééééééééééééééééééééééééééééééééééééééééééééééééééééééééééééééééééééééééééééééééééééééééééééééééééééééééééééééééééééééééééééééééééééééééééééééééééééééééééééééééééééééééééééééééééééééééééééééééééééééééééééééééééééééééééééééééééééééééééééééééééééééééééééééééééééééééééééééééééééééééééééééééééééééééééééééééééééééééééééééééééééééééééééééééééééééééééééééééééééééééééééééééééééééééééééééééééééééééééééééééééééééééééééééééééééééééééééééééééééééééééééééééééééééééééééééééééééééééééééééééééééééééééééééééééééééééééééééééééééééééééééééééééééééééééééééééééééééééééééééééééééééééééééééééééééééééééééééééééééééééééééééééééééééééééééééééééééééééééééééééééééééééééééééééééééééééééééééééééééééééééééééééééééééééééééééééééééééééééééééééééééééééééééééééééééééééééééééééééééééééééééééééééééééééééééééééééééééééééééééééééééééééééééééééééééééééééééééééééééééééééééééééééééééééééééééééééééééééééééééééééééééééééééééééééééééééééééééééééééééééééééééééééééééééééééééééééééééééééééééééééééééééééééééééééééééééééééééééééééééééééééééééééééééééééééééééééééééééééééééééééééééééééééééééééééééééééééééééééééééééééééééééééééééééééééééééééééééééééééééééééééééééééééééééééééééééééééééééééééééééééééééééééééééééééééééééééééééééééééééééééééééééééééééééééééééééééééééééééééééééééééééééééééééééééééééééééééééééééééééééééééééééééééééééééééééééééééééééééééééééééééééééééééééééééééééééééééééééééééééééééééééééééééééééééééééééééééééééééééééééééééééééééééééééééééééééééééééééééééééééééééééééééééééééééééééééééééééééééééééééééééééééééééééééééééééééééééééééééééééééééééééééééééééééééééééééééééééééééééééééééééééééééééééééééééééééééééééééééééééééééééééééééééééééééééééééééééééééééééééééééééééééééééééééééééééééééééééééééééééééééééééééééééééééééééééééééééééééééééééééééééééééééééééééééééééééééééééééééééééééééééééééééééééééééééééééééééééééééééééééééééééééééééééééééééééééééééééééééééééééééééééééééééééééééééééééééééééééééééééééééééééééééééééééééééééééééééééééééééééééééééééééééééééééééééééééééééééééééééééééééééééééééééééééééééééééééééééééééééééééééééééééééééééééééééééééééééééééééééééééééééééééééééééééééééééééééééééééééééééééééééééééééééééééééééééééééééééééééééééééééééééééééééééééééééééééééééééééééééééééééééééééééééééééééééééééééééééééééééééééééééééééééééééééééééééééééééééééééééééééééééééééééééééééééééééééééa"],
|
|
47
|
+
"environment": {
|
|
48
|
+
"CI": "true",
|
|
49
|
+
"SHIPZONE_CI_INPUTS_JSON": "{}",
|
|
50
|
+
"SHIPZONE_CI_MATRIX_JSON": "{}"
|
|
51
|
+
}
|
|
52
|
+
}
|
|
53
|
+
],
|
|
54
|
+
"source": {
|
|
55
|
+
"format": "tar.gz",
|
|
56
|
+
"sha256": "eeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeee",
|
|
57
|
+
"sizeBytes": 1024,
|
|
58
|
+
"extractedSizeBytes": 4096,
|
|
59
|
+
"entryCount": 4
|
|
60
|
+
},
|
|
61
|
+
"secrets": {},
|
|
62
|
+
"timeoutMs": 60000,
|
|
63
|
+
"resources": {
|
|
64
|
+
"memoryBytes": 268435456,
|
|
65
|
+
"cpus": 1,
|
|
66
|
+
"pids": 256
|
|
67
|
+
},
|
|
68
|
+
"artifacts": [],
|
|
69
|
+
"caches": []
|
|
70
|
+
}
|
|
@@ -3,7 +3,7 @@
|
|
|
3
3
|
*/
|
|
4
4
|
export const commitinfo = {
|
|
5
5
|
name: '@ship.zone/ci-spec',
|
|
6
|
-
version: '2.1.
|
|
6
|
+
version: '2.1.2',
|
|
7
7
|
description: 'The ship.zone CI standard: language-neutral CI workflow and runner protocol specifications.'
|
|
8
8
|
};
|
|
9
9
|
//# sourceMappingURL=data:application/json;base64,eyJ2ZXJzaW9uIjozLCJmaWxlIjoiMDBfY29tbWl0aW5mb19kYXRhLmpzIiwic291cmNlUm9vdCI6IiIsInNvdXJjZXMiOlsiLi4vdHMvMDBfY29tbWl0aW5mb19kYXRhLnRzIl0sIm5hbWVzIjpbXSwibWFwcGluZ3MiOiJBQUFBOztHQUVHO0FBQ0gsTUFBTSxDQUFDLE1BQU0sVUFBVSxHQUFHO0lBQ3hCLElBQUksRUFBRSxvQkFBb0I7SUFDMUIsT0FBTyxFQUFFLE9BQU87SUFDaEIsV0FBVyxFQUFFLDZGQUE2RjtDQUMzRyxDQUFBIn0=
|
package/package.json
CHANGED
package/readme.md
CHANGED
|
@@ -12,7 +12,7 @@ The package version is the specification version, and it is the only version in
|
|
|
12
12
|
spec: 2.0.0
|
|
13
13
|
```
|
|
14
14
|
|
|
15
|
-
Compiled jobs and runner protocol messages carry the same `spec` field. An implementation built against version `I` accepts a declared version `D` when both have the same major version and `D` is not newer than `I`; the rule is defined in `spec/runner-protocol.md` and fixed by `conformance/version-cases.json`. Every incompatible change is a new major version, and additions arrive as new minor versions: a workflow that uses a construct introduced in a later minor version declares that version or a later one. Version 2.0.0 renames the package and its normative identifiers (see Migrating from @foss.global/ci-spec), removes the 1.x cross-version rules, and completes the compilation rules: one stable failure code per compilation failure with a fixed precedence, a stricter YAML subset, the derivation of every compiled job member, and the identities of expanded nodes. Version 2.1.0 adds job failure codes: every coherence rule of a compiled job has exactly one code, and a job that is not coherent carries the code of the first rule it violates (see Job Coherence in `spec/runner-protocol.md`); it also exports the reserved names as constants. Documents written against 2.0.0 stay valid. The history of the 1.x rules is in the changelog.
|
|
15
|
+
Compiled jobs and runner protocol messages carry the same `spec` field. An implementation built against version `I` accepts a declared version `D` when both have the same major version and `D` is not newer than `I`; the rule is defined in `spec/runner-protocol.md` and fixed by `conformance/version-cases.json`. Every incompatible change is a new major version, and additions arrive as new minor versions: a workflow that uses a construct introduced in a later minor version declares that version or a later one. Version 2.0.0 renames the package and its normative identifiers (see Migrating from @foss.global/ci-spec), removes the 1.x cross-version rules, and completes the compilation rules: one stable failure code per compilation failure with a fixed precedence, a stricter YAML subset, the derivation of every compiled job member, and the identities of expanded nodes. Version 2.1.0 adds job failure codes: every coherence rule of a compiled job has exactly one code, and a job that is not coherent carries the code of the first rule it violates (see Job Coherence in `spec/runner-protocol.md`); it also exports the reserved names as constants. Version 2.1.1 clarifies compilation: it states the YAML 1.2 character, byte order mark, escape, and document counting rules, bounds collection nesting to 64, and counts the 16 KiB command argument limit in UTF-8 bytes. Version 2.1.2 settles readings the text left open: separate artifact and cache name spaces, the expanded jobs and step environment names that count toward their limits, exact npm registry comparison, egress wildcards at any depth, the npm delivery names reserved in every job profile, the declared values `resources_invalid` compares, informative failure locations, carriage return line breaks, and byte order mark lines after keep-chomped block scalars. Documents written against 2.0.0 stay valid. The history of the 1.x rules is in the changelog.
|
|
16
16
|
|
|
17
17
|
## Issue Reporting and Security
|
|
18
18
|
|
package/spec/ci-actions.md
CHANGED
|
@@ -10,9 +10,11 @@ Statements that no conformance case can observe are marked *(non-testable)*.
|
|
|
10
10
|
|
|
11
11
|
The coordinator reads the workflow as bytes from the exact source commit being executed. A file of more than 262,144 bytes (256 KiB) fails with `workflow_too_large`, and bytes that are not well-formed UTF-8 fail with `workflow_encoding_invalid`. The file may begin with one byte order mark (U+FEFF), which is not part of the document; the size limit and the raw workflow SHA-256 count every byte as read, including it.
|
|
12
12
|
|
|
13
|
+
Lines end as in YAML 1.2: a line feed, a carriage return followed by a line feed, and a carriage return that no line feed follows are each one line break, and every line break in scalar content is read as a line feed. A file whose lines end in carriage returns alone therefore holds the same document as the same file with line feeds; the size limit and the raw workflow SHA-256 still count its bytes as read.
|
|
14
|
+
|
|
13
15
|
Parsing uses YAML 1.2 with the core schema and requires:
|
|
14
16
|
|
|
15
|
-
- exactly one document
|
|
17
|
+
- exactly one document. As in YAML 1.2, a `---` directives end marker begins a document, and so does content that no marker precedes; a document end marker `...`, a comment, a blank line, and a byte order mark begin none. An empty file, a file of only comments or only `...` markers, and a file with a second document fail with `workflow_yaml_document_count`. A document followed by several `...` markers is one document, and a file of only `--- # comment` is one empty document, whose null value fails schema validation;
|
|
16
18
|
- no YAML or TAG directive (`workflow_yaml_directive`);
|
|
17
19
|
- unique keys in every mapping (`workflow_yaml_duplicate_key`);
|
|
18
20
|
- string mapping keys: every key is a scalar whose core-schema resolution is a string, so a plain key such as `1`, `true`, `null`, or `~`, and every collection used as a key, fails with `workflow_yaml_key_type`; such a key is written quoted, for example `"1"`;
|
|
@@ -20,6 +22,13 @@ Parsing uses YAML 1.2 with the core schema and requires:
|
|
|
20
22
|
- no merge key: a `<<` key, plain or quoted, fails with `workflow_yaml_merge_key`;
|
|
21
23
|
- no explicit tag, including the standard `!!` tags and the non-specific tag `!` (`workflow_yaml_tag`).
|
|
22
24
|
|
|
25
|
+
The following YAML 1.2 rules fail with `workflow_yaml_syntax`:
|
|
26
|
+
|
|
27
|
+
- Characters. A C0 control character other than tab, line feed, and carriage return is allowed nowhere, not even in a quoted scalar. DEL (U+007F), a C1 control character other than NEL (U+0080 to U+009F except U+0085), U+FFFE, U+FFFF, and U+FEFF are allowed inside a single- or double-quoted scalar, where they are content, and nowhere else, except where U+FEFF is a byte order mark. Unpaired surrogates cannot occur, because the file is well-formed UTF-8.
|
|
28
|
+
- Byte order marks. One or more U+FEFF at the start of a line are byte order marks, which are not content, when the line comes before the one on which the document begins, is that line, or comes after a document end marker `...`. They are also byte order marks when the line comes after the document's content and it and every later line up to the end of the file, or up to the next `---` or `...` marker, are blank or comment lines: YAML 1.2 lets a document prefix follow a document without a `...` marker, and a `---` after it begins a second document. A line that begins with U+FEFF is neither an empty line nor a content line of a block scalar, so it ends a block scalar as a comment line less indented than the scalar does: after a block scalar with keep chomping (`|+` or `>+`), the empty lines before such a line are content of the scalar, and the line and every line after it are not. The file's own leading byte order mark is the first of them. Any other U+FEFF outside a quoted scalar, for example in a plain or block scalar, in a comment, or at the start of a line within the document or followed by content, fails.
|
|
29
|
+
- Escapes. Every escape in a double-quoted scalar denotes a Unicode scalar value, so every string can be serialized with RFC 8785. A `\u` escape of a high surrogate (`\uD800` to `\uDBFF`) directly followed by a `\u` escape of a low surrogate (`\uDC00` to `\uDFFF`) denotes the one character the pair encodes, as in JSON: `"\uD83D\uDE00"` is U+1F600. Every other escape of a surrogate code point fails, such as a lone `\uD800` or `\uDC00` or the escape `\U0000D800`. Escapes of other characters, including control characters such as `\0` and `\x7F`, are content.
|
|
30
|
+
- Nesting depth. Collections nest at most 64 deep: the root collection has depth 1, and a collection that is an entry of a sequence, or a key or value of a mapping, of depth *d* has depth *d* + 1. A collection of depth 65 fails, so a parser can stop there instead of exhausting its stack. The deepest collection of a schema-valid workflow, an alias of `publish.images[].destinations[].aliases`, has depth 8.
|
|
31
|
+
|
|
23
32
|
Every other parser error or warning fails with `workflow_yaml_syntax`. The version check (see Specification Version) and JSON Schema validation against `schemas/ci_actions.schema.json`, which fails with `workflow_schema_invalid`, run only after these YAML-level rules pass.
|
|
24
33
|
|
|
25
34
|
Every plain scalar that the core schema resolves to an integer or a float is converted from its original token before schema validation; quoted scalars are strings and never numbers:
|
|
@@ -166,9 +175,9 @@ Jobs form an acyclic graph through `needs`. A dependency on a job the workflow d
|
|
|
166
175
|
|
|
167
176
|
Steps use argument arrays. Shell strings, arbitrary expression languages, GitHub action compatibility, and unpinned third-party actions are not supported. The coordinator preserves declaration order and emits an ordered compiled `steps` array. Each compiled step contains its command and the complete merged non-secret environment; runners do not perform workflow-level environment merging.
|
|
168
177
|
|
|
169
|
-
Environment maps are merged in workflow, job, then step order: a name defined at more than one of these levels takes the value of the innermost one. The workflow environment applies only to the steps of `oci` and `vm` jobs. Secret references are job-wide and are injected into every step only after trust evaluation. A secret target equal to a name of the merged environment of any step of the job, two secret references with one target, and any `SHIPZONE_CI_*` name in an environment map or as a secret target fail with `environment_conflict`.
|
|
178
|
+
Environment maps are merged in workflow, job, then step order: a name defined at more than one of these levels takes the value of the innermost one. The workflow environment applies only to the steps of `oci` and `vm` jobs. Secret references are job-wide and are injected into every step only after trust evaluation. A secret target equal to a name of the merged environment of any step of the job, two secret references with one target, and any `SHIPZONE_CI_*` name in an environment map or as a secret target fail with `environment_conflict`. The names a step receives are the union of the names of its merged environment, the secret targets of its job, and the reserved names (see the runner protocol's OCI Container Profile): `SHIPZONE_CI_MATRIX_JSON`, `SHIPZONE_CI_INPUTS_JSON`, and, in a job with `npmRead`, `NPM_CONFIG_USERCONFIG`. Each name counts once, however many of these sources contain it, so a declared `NPM_CONFIG_USERCONFIG` in a job with `npmRead`, which fails with `npm_read_invalid` (see npm Read Access), adds no name. A step that receives more than 128 names fails with `environment_limit_exceeded`. A step with a command argument of more than 16,384 UTF-8 bytes, or whose command arguments total more than 131,072 UTF-8 bytes, fails with `command_limit_exceeded`; the schema bounds an argument to 16,384 characters, which can exceed 16,384 bytes.
|
|
170
179
|
|
|
171
|
-
Artifact
|
|
180
|
+
Artifact names must be unique among the artifacts of a job, and cache names among its caches; a repeated name fails with `transfer_name_duplicate`. Artifacts and caches are separate name spaces, because a transfer is identified by its kind and its name (see the runner protocol's Transfers and Integrity), so an artifact and a cache of one job may share a name. Cache paths must not overlap each other: two paths overlap when, split at `/` with empty and `.` segments removed, the segments of one are a prefix of the segments of the other. Overlapping paths fail with `cache_path_overlap`. Artifacts and caches are `tar.gz` archives with explicit compressed-byte, extracted-byte, and entry ceilings. Produced digest and size metadata is measured at transfer time and is not part of the compiled declaration.
|
|
172
181
|
|
|
173
182
|
A cache `key` is a literal and compiles unchanged. The cache namespace of a run is tenant, repository, trust class, and whether the run is a protected tag run (see Triggers): protected tag runs have a namespace of their own, which they alone read and write, and they neither read nor write the namespace of any other trusted run. A cache a protected tag run restores was therefore written by a protected tag run of the same repository. Within one namespace, every job that declares the same key, including every expansion of one matrix job, reads and writes the same cache; the last successful publication wins. A workflow that needs separate caches, for example one per compilation target, declares separate keys in separate jobs.
|
|
174
183
|
|
|
@@ -190,7 +199,7 @@ Limits count every job of the workflow, whether or not a run skips it.
|
|
|
190
199
|
|
|
191
200
|
## Resources
|
|
192
201
|
|
|
193
|
-
`resources` is optional on every job and applies to every job the declaration expands to. `memoryBytes`, `cpus`, `pids`, and `sharedMemoryBytes` compile unchanged into the compiled job `resources`; `workspaceBytes` and `workspaceEntries` compile into `requirements.limits.maximumWorkspaceBytes` and `requirements.limits.maximumWorkspaceEntries`. An omitted value takes the deployment default. `sharedMemoryBytes` greater than `memoryBytes` of the same job fails with `resources_invalid`.
|
|
202
|
+
`resources` is optional on every job and applies to every job the declaration expands to. `memoryBytes`, `cpus`, `pids`, and `sharedMemoryBytes` compile unchanged into the compiled job `resources`; `workspaceBytes` and `workspaceEntries` compile into `requirements.limits.maximumWorkspaceBytes` and `requirements.limits.maximumWorkspaceEntries`. An omitted value takes the deployment default. `sharedMemoryBytes` greater than `memoryBytes` of the same job fails with `resources_invalid`. The rule compares declared values only and does not apply when a job omits either of them: the `workflow` layer depends only on the workflow bytes (see Compilation Failures), and the compiled job carries only declared values (see Compiled Jobs). A `sharedMemoryBytes` above the deployment's default memory is therefore not a `workflow` layer failure; a deployment may bound it with a policy limit (see Limits), and a runner receives it only when it fits the runner's capabilities (see the runner protocol's Capabilities and Matching).
|
|
194
203
|
|
|
195
204
|
The runner enforces each value as a ceiling for the whole attempt: `memoryBytes` for all its processes, `cpus` as CPU time in units of one CPU, `pids` as the number of concurrent processes and threads, and `sharedMemoryBytes` as the size of `/dev/shm` inside the sandbox. Resources are matched against runner capabilities as defined by the runner protocol.
|
|
196
205
|
|
|
@@ -230,15 +239,15 @@ A coordinator never delivers a value that is not admissible, for example one it
|
|
|
230
239
|
|
|
231
240
|
The following fail with `npm_read_invalid`:
|
|
232
241
|
|
|
233
|
-
- a registry listed twice, or a scope listed under two registries;
|
|
234
|
-
- a registry whose host and port, 443 unless the URL names one, no entry of the job's `egress` allowlist permits: npm read access adds no network access;
|
|
235
|
-
- in a job with `npmRead`,
|
|
242
|
+
- a registry listed twice, or a scope listed under two registries. Registries are compared as exact strings, without normalization, for example of a trailing `/` or an explicit port 443: each entry receives its own grant, which carries the entry's registry as written (see the runner protocol's npm Read Grants);
|
|
243
|
+
- a registry whose host and port, 443 unless the URL names one, no entry of the job's `egress` allowlist permits: npm read access adds no network access. An entry permits a host as the runner protocol's Network defines: an exact entry the host itself, and a `*.` entry every name below its domain, at any depth;
|
|
244
|
+
- in a job with `npmRead`, whatever its profile, a secret target named `NPM_CONFIG_USERCONFIG`, a step whose merged environment defines `NPM_CONFIG_USERCONFIG`, or a build secret with id `npmrc`. The merged environment of an `oci` or `vm` step includes the workflow environment, which does not apply to build jobs (see Jobs and Steps). Both names are reserved for grant delivery, and the reserved environment name counts toward the 128 names of every step.
|
|
236
245
|
|
|
237
246
|
Deployment policy may reject a registry for which it cannot issue grants; compilation then fails with `policy_npm_registry_denied`. npm read grants are issued only to trusted runs: compiling an untrusted run in which a job that is not skipped declares `npmRead` fails with code `npm_read_untrusted`. Every pull request run is untrusted, whatever its author (see Trust), so a workflow that installs private scopes restricts those jobs with `triggers` or declares no `pullRequest` trigger.
|
|
238
247
|
|
|
239
248
|
## Image Builds
|
|
240
249
|
|
|
241
|
-
A build job compiles to one `oci-image` runner job per entry of `build.platforms`, in declared order, plus one coordinator assembly node named after the job. Each
|
|
250
|
+
A build job compiles to one `oci-image` runner job per entry of `build.platforms`, in declared order, plus one coordinator assembly node named after the job. Each per-platform build job counts toward the expanded-job ceiling; the assembly node is a coordinator node, not a job, and does not count. Build jobs have no matrix.
|
|
242
251
|
|
|
243
252
|
- `context` and `dockerfile` are paths relative to the repository root; `target` selects a Dockerfile stage. Every entry of `platforms` is a `linux` platform.
|
|
244
253
|
- `buildArgs` are non-secret values compiled into the plan and possibly persisted in image history. Names starting with `BUILDKIT_` are rejected.
|
|
@@ -336,7 +345,7 @@ Every job node compiles to one compiled job, a function of the workflow, the run
|
|
|
336
345
|
|
|
337
346
|
- Workflow source: 256 KiB.
|
|
338
347
|
- Jobs before expansion: 128.
|
|
339
|
-
- Expanded jobs: 256.
|
|
348
|
+
- Expanded jobs: 256, counting the job nodes of the Expanded DAG and not the candidate assembly nodes.
|
|
340
349
|
- Steps per job: 128.
|
|
341
350
|
- Dependencies per job: 32.
|
|
342
351
|
- Matrix dimensions: 8.
|
|
@@ -351,7 +360,7 @@ Every job node compiles to one compiled job, a function of the workflow, the run
|
|
|
351
360
|
- Secret references: 128 per job.
|
|
352
361
|
- Artifacts: 64 per job.
|
|
353
362
|
- Caches: 32 per job.
|
|
354
|
-
- Command: 128 arguments, 16 KiB each, 128 KiB total.
|
|
363
|
+
- Command: 128 arguments, 16 KiB each, 128 KiB total after UTF-8 encoding.
|
|
355
364
|
- Artifact archive: 16 GiB compressed, 128 GiB extracted, 1,000,000 entries per item.
|
|
356
365
|
- Cache archive: 16 GiB compressed, 128 GiB extracted, 1,000,000 entries per item.
|
|
357
366
|
- Workspace: 1 TiB and 2,000,000 entries.
|
|
@@ -364,11 +373,13 @@ All objects reject unknown properties. Implementations may use lower deployment
|
|
|
364
373
|
|
|
365
374
|
A compilation failure fails the run before any job is enqueued and carries exactly one code. The codes are grouped into layers, which the coordinator evaluates in the order of the table; the first layer with a violation decides the failure. Within the `source`, `yaml`, `version`, and `schema` layers the failure carries the code of any violation of that layer. Within the `workflow`, `policy`, and `run` layers it carries the first code, in table order, whose rule the workflow, run, and policy violate. Trigger matching (see Triggers) happens between the `workflow` and `policy` layers, so a run that is not triggered is never evaluated against the later layers. The `source` through `workflow` layers depend only on the workflow bytes, so every run of one commit fails them alike.
|
|
366
375
|
|
|
376
|
+
The code is the only normative part of a failure. A coordinator may report diagnostic details with it, such as the location of a violation in the workflow and a message; they are informative, may differ between conforming coordinators, and no conformance case fixes them. When the deciding layer or rule is violated more than once, the specification does not fix which violation such details describe.
|
|
377
|
+
|
|
367
378
|
| Layer | Code | Rule |
|
|
368
379
|
| --- | --- | --- |
|
|
369
380
|
| source | `workflow_too_large` | the file has more than 262,144 bytes (Parsing) |
|
|
370
381
|
| source | `workflow_encoding_invalid` | the file is not well-formed UTF-8 (Parsing) |
|
|
371
|
-
| yaml | `workflow_yaml_syntax` | any other YAML parser error or warning (Parsing) |
|
|
382
|
+
| yaml | `workflow_yaml_syntax` | a character, byte order mark, escape, or nesting depth that YAML 1.2 or Parsing rejects, or any other YAML parser error or warning (Parsing) |
|
|
372
383
|
| yaml | `workflow_yaml_document_count` | no document, or more than one (Parsing) |
|
|
373
384
|
| yaml | `workflow_yaml_directive` | a YAML or TAG directive (Parsing) |
|
|
374
385
|
| yaml | `workflow_yaml_duplicate_key` | a mapping repeats a key (Parsing) |
|
|
@@ -389,10 +400,10 @@ A compilation failure fails the run before any job is enqueued and carries exact
|
|
|
389
400
|
| workflow | `permission_insufficient` | a declaration or build job beyond its resolved permissions (Permissions) |
|
|
390
401
|
| workflow | `environment_conflict` | a secret target or reserved-name conflict (Jobs and Steps) |
|
|
391
402
|
| workflow | `environment_limit_exceeded` | more than 128 names in a step (Jobs and Steps) |
|
|
392
|
-
| workflow | `command_limit_exceeded` | a command above 131,072 bytes (Jobs and Steps) |
|
|
393
|
-
| workflow | `transfer_name_duplicate` | a repeated
|
|
403
|
+
| workflow | `command_limit_exceeded` | a command argument above 16,384 bytes or a command above 131,072 bytes (Jobs and Steps) |
|
|
404
|
+
| workflow | `transfer_name_duplicate` | a name repeated among the artifacts or among the caches of a job (Jobs and Steps) |
|
|
394
405
|
| workflow | `cache_path_overlap` | overlapping cache paths (Jobs and Steps) |
|
|
395
|
-
| workflow | `resources_invalid` | `sharedMemoryBytes` above `memoryBytes` (Resources) |
|
|
406
|
+
| workflow | `resources_invalid` | a declared `sharedMemoryBytes` above the declared `memoryBytes` of its job (Resources) |
|
|
396
407
|
| workflow | `network_host_duplicate` | a repeated egress host (Network) |
|
|
397
408
|
| workflow | `npm_read_invalid` | a broken `npmRead` rule (npm Read Access) |
|
|
398
409
|
| workflow | `build_input_conflict` | a broken build argument or build secret rule (Image Builds) |
|
package/spec/runner-protocol.md
CHANGED
|
@@ -127,6 +127,7 @@ A compiled job is *coherent* when it violates no rule of the following table. Th
|
|
|
127
127
|
| `job_environment_incoherent` | a secret target that starts with `SHIPZONE_CI_`, or a step environment name that starts with it and is not `SHIPZONE_CI_MATRIX_JSON` or `SHIPZONE_CI_INPUTS_JSON` (OCI Container Profile) |
|
|
128
128
|
| `job_environment_incoherent` | an `oci` or `vm` step whose environment lacks `SHIPZONE_CI_MATRIX_JSON` or `SHIPZONE_CI_INPUTS_JSON`, or whose value of one of them is not the RFC 8785 serialization of a JSON object (OCI Container Profile) |
|
|
129
129
|
| `job_environment_incoherent` | an `oci` or `vm` step whose environment names, the job's secret targets, and, in a job with `npmRead`, `NPM_CONFIG_USERCONFIG` are not unique or number more than 128 (OCI Container Profile, Protocol Limits) |
|
|
130
|
+
| `job_command_limit_exceeded` | a step command argument of more than 16,384 UTF-8 bytes; `schemas/runner-job.schema.json` bounds an argument only to 16,384 characters (Protocol Limits) |
|
|
130
131
|
| `job_command_limit_exceeded` | a step command whose arguments total more than 131,072 UTF-8 bytes (Protocol Limits) |
|
|
131
132
|
| `job_secret_inadmissible` | a value of `secrets` shorter than 8 UTF-8 bytes (Secret Redaction) |
|
|
132
133
|
| `job_digest_mismatch` | a `compiledPlanDigest` other than the digest Canonical Digests defines for the job |
|
|
@@ -275,7 +276,7 @@ Every attempt has network namespaces of its own, shared with no other attempt an
|
|
|
275
276
|
`network` is part of the compiled job and the plan digest. Its modes are:
|
|
276
277
|
|
|
277
278
|
- `none`: no traffic leaves the sandbox. This is the default: a workflow job without `network` compiles to `{ "mode": "none" }`.
|
|
278
|
-
- `egress`: outbound access only to the listed `allow` entries. Each entry names one lowercase DNS host, either exact or `*.` followed by at least two labels, and its TCP ports. `*.example.com` matches
|
|
279
|
+
- `egress`: outbound access only to the listed `allow` entries. Each entry names one lowercase DNS host, either exact or `*.` followed by at least two labels, and its TCP ports. `*.example.com` matches every name below `example.com`, at any depth, such as `a.example.com` and `a.b.example.com`, but not `example.com` itself. IP literals and single-label names are invalid.
|
|
279
280
|
|
|
280
281
|
An empty allowlist cannot be expressed: `egress` requires at least one entry, `none` is the only representation of no network, and no implicit allowlist exists. Repository templates that need network access declare `egress` with the hosts they need.
|
|
281
282
|
|
package/spec/runner.openapi.json
CHANGED
|
@@ -2,7 +2,7 @@
|
|
|
2
2
|
"openapi": "3.1.0",
|
|
3
3
|
"info": {
|
|
4
4
|
"title": "ship.zone CI Runner Protocol",
|
|
5
|
-
"version": "2.1.
|
|
5
|
+
"version": "2.1.2",
|
|
6
6
|
"description": "Language-neutral, outbound runner protocol of @ship.zone/ci-spec. info.version is the specification version this description is written against. This draft is incompatible with the legacy internal runner protocol it replaces.",
|
|
7
7
|
"license": {
|
|
8
8
|
"name": "MIT",
|