@opensoftware/contracts 0.0.0-stage → 1.0.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/LICENSE +202 -0
- package/README.md +105 -2
- package/dist/breaking.d.ts +27 -0
- package/dist/breaking.js +156 -0
- package/dist/cli.d.ts +2 -0
- package/dist/cli.js +68 -0
- package/dist/formats.d.ts +10 -0
- package/dist/formats.js +18 -0
- package/dist/generated/types.d.ts +543 -0
- package/dist/generated/types.js +1 -0
- package/dist/index.d.ts +28 -0
- package/dist/index.js +73 -0
- package/dist/roundtrip.d.ts +3 -0
- package/dist/roundtrip.js +89 -0
- package/events/asyncapi.yaml +144 -0
- package/events/gate-decided/gate-decided.schema.json +122 -0
- package/events/gate-requested/gate-requested.schema.json +135 -0
- package/events/pr-opened/pr-opened.schema.json +138 -0
- package/events/run-completed/run-completed.schema.json +163 -0
- package/events/score-recorded/score-recorded.schema.json +163 -0
- package/events/work-received/work-received.schema.json +182 -0
- package/evidence-bundle/evidence-bundle.schema.json +180 -0
- package/factory-definition/factory-definition.schema.json +371 -0
- package/openapi/openapi.json +1553 -0
- package/package.json +50 -3
- package/run-record/run-record.schema.json +179 -0
- package/score/score.schema.json +110 -0
- package/typespec/common.tsp +62 -0
- package/typespec/events.tsp +134 -0
- package/typespec/evidence-bundle.tsp +99 -0
- package/typespec/factory-definition.tsp +193 -0
- package/typespec/main.tsp +10 -0
- package/typespec/run-record.tsp +88 -0
- package/typespec/score.tsp +68 -0
- package/typespec/work-item.tsp +65 -0
- package/work-item/work-item.schema.json +129 -0
|
@@ -0,0 +1,89 @@
|
|
|
1
|
+
// `osf-contracts roundtrip --client <client> [dir]`: every valid fixture under dir is loaded by a generated client
|
|
2
|
+
// and written back; the result must equal the fixture. TypeScript is the only client (ADR 0012); the flag leaves
|
|
3
|
+
// room for another one.
|
|
4
|
+
import { existsSync, readdirSync, readFileSync, statSync } from "node:fs";
|
|
5
|
+
import { join, sep } from "node:path";
|
|
6
|
+
import { FORMATS } from "./formats.js";
|
|
7
|
+
import { validate } from "./index.js";
|
|
8
|
+
export const CLIENTS = ["typescript"];
|
|
9
|
+
export function roundtrip(_client, dir) {
|
|
10
|
+
const fixtures = validFixtures(dir);
|
|
11
|
+
if (fixtures.length === 0) {
|
|
12
|
+
process.stderr.write(`No valid fixtures under ${dir}/<format>/fixtures/valid/\n`);
|
|
13
|
+
return 1;
|
|
14
|
+
}
|
|
15
|
+
let written;
|
|
16
|
+
try {
|
|
17
|
+
written = viaTypeScript(fixtures);
|
|
18
|
+
}
|
|
19
|
+
catch (error) {
|
|
20
|
+
process.stderr.write(`${error.message}\n`);
|
|
21
|
+
return 1;
|
|
22
|
+
}
|
|
23
|
+
let changed = 0;
|
|
24
|
+
for (const { file } of fixtures) {
|
|
25
|
+
const original = JSON.parse(readFileSync(file, "utf8"));
|
|
26
|
+
const difference = written.has(file) ? firstDifference(original, written.get(file), "") : "not written back";
|
|
27
|
+
if (difference === undefined) {
|
|
28
|
+
process.stdout.write(`${file}: unchanged\n`);
|
|
29
|
+
}
|
|
30
|
+
else {
|
|
31
|
+
changed++;
|
|
32
|
+
process.stdout.write(`${file}: changed at ${difference}\n`);
|
|
33
|
+
}
|
|
34
|
+
}
|
|
35
|
+
return changed === 0 ? 0 : 1;
|
|
36
|
+
}
|
|
37
|
+
function validFixtures(dir) {
|
|
38
|
+
const out = [];
|
|
39
|
+
for (const format of FORMATS) {
|
|
40
|
+
const folder = join(dir, format.path, "fixtures", "valid");
|
|
41
|
+
if (!existsSync(folder) || !statSync(folder).isDirectory())
|
|
42
|
+
continue;
|
|
43
|
+
for (const entry of readdirSync(folder).sort()) {
|
|
44
|
+
if (entry.endsWith(".json"))
|
|
45
|
+
out.push({ file: join(folder, entry).split(sep).join("/"), format });
|
|
46
|
+
}
|
|
47
|
+
}
|
|
48
|
+
return out;
|
|
49
|
+
}
|
|
50
|
+
/** The TypeScript side: validate, then serialize the typed value again. */
|
|
51
|
+
function viaTypeScript(fixtures) {
|
|
52
|
+
const out = new Map();
|
|
53
|
+
for (const { file, format } of fixtures) {
|
|
54
|
+
const document = JSON.parse(readFileSync(file, "utf8"));
|
|
55
|
+
const problems = validate(format, document);
|
|
56
|
+
if (problems.length > 0)
|
|
57
|
+
throw new Error(`${file} does not match ${format.name}: ${problems[0]?.path} ${problems[0]?.message}`);
|
|
58
|
+
out.set(file, JSON.parse(JSON.stringify(document)));
|
|
59
|
+
}
|
|
60
|
+
return out;
|
|
61
|
+
}
|
|
62
|
+
/** JSON Pointer and values of the first difference; numbers compare by value, so 4 equals 4.0. */
|
|
63
|
+
function firstDifference(a, b, path) {
|
|
64
|
+
if (typeof a === "number" && typeof b === "number")
|
|
65
|
+
return a === b ? undefined : `${path || "/"}: ${a} → ${b}`;
|
|
66
|
+
if (Array.isArray(a) && Array.isArray(b)) {
|
|
67
|
+
if (a.length !== b.length)
|
|
68
|
+
return `${path || "/"}: ${a.length} items → ${b.length} items`;
|
|
69
|
+
for (let index = 0; index < a.length; index++) {
|
|
70
|
+
const found = firstDifference(a[index], b[index], `${path}/${index}`);
|
|
71
|
+
if (found)
|
|
72
|
+
return found;
|
|
73
|
+
}
|
|
74
|
+
return undefined;
|
|
75
|
+
}
|
|
76
|
+
if (a && b && typeof a === "object" && typeof b === "object" && !Array.isArray(a) && !Array.isArray(b)) {
|
|
77
|
+
const left = a;
|
|
78
|
+
const right = b;
|
|
79
|
+
for (const key of new Set([...Object.keys(left), ...Object.keys(right)])) {
|
|
80
|
+
const found = firstDifference(left[key], right[key], `${path}/${key}`);
|
|
81
|
+
if (found)
|
|
82
|
+
return found;
|
|
83
|
+
}
|
|
84
|
+
return undefined;
|
|
85
|
+
}
|
|
86
|
+
return JSON.stringify(a) === JSON.stringify(b)
|
|
87
|
+
? undefined
|
|
88
|
+
: `${path || "/"}: ${JSON.stringify(a)} → ${JSON.stringify(b)}`;
|
|
89
|
+
}
|
|
@@ -0,0 +1,144 @@
|
|
|
1
|
+
# Events between modules (ADR 0006). Hand-written: TypeSpec has no maintained AsyncAPI emitter, so the
|
|
2
|
+
# payloads are the JSON Schemas that `pnpm build` emits from typespec/events.tsp. Every message is a CloudEvents
|
|
3
|
+
# 1.0 event in structured JSON mode; its `type` is the versioned name, for example "work.received@v1".
|
|
4
|
+
asyncapi: 3.1.0
|
|
5
|
+
info:
|
|
6
|
+
title: Open Software Factory events
|
|
7
|
+
version: 0.0.0
|
|
8
|
+
description: Facts that modules publish and others react to. Past tense, versioned, at-least-once.
|
|
9
|
+
defaultContentType: application/cloudevents+json
|
|
10
|
+
servers:
|
|
11
|
+
nats:
|
|
12
|
+
host: localhost:4222
|
|
13
|
+
protocol: nats
|
|
14
|
+
description: NATS JetStream (ADR 0003). Without a bus the same JSON is posted to webhooks.
|
|
15
|
+
channels:
|
|
16
|
+
work-received:
|
|
17
|
+
address: osf.work.received.v1
|
|
18
|
+
description: A work item was accepted. `data` is a work item.
|
|
19
|
+
messages:
|
|
20
|
+
WorkReceived:
|
|
21
|
+
$ref: '#/components/messages/WorkReceived'
|
|
22
|
+
run-completed:
|
|
23
|
+
address: osf.run.completed.v1
|
|
24
|
+
description: A run ended.
|
|
25
|
+
messages:
|
|
26
|
+
RunCompleted:
|
|
27
|
+
$ref: '#/components/messages/RunCompleted'
|
|
28
|
+
gate-requested:
|
|
29
|
+
address: osf.gate.requested.v1
|
|
30
|
+
description: A stage waits for a decision.
|
|
31
|
+
messages:
|
|
32
|
+
GateRequested:
|
|
33
|
+
$ref: '#/components/messages/GateRequested'
|
|
34
|
+
gate-decided:
|
|
35
|
+
address: osf.gate.decided.v1
|
|
36
|
+
description: A gate was approved, rejected or expired.
|
|
37
|
+
messages:
|
|
38
|
+
GateDecided:
|
|
39
|
+
$ref: '#/components/messages/GateDecided'
|
|
40
|
+
score-recorded:
|
|
41
|
+
address: osf.score.recorded.v1
|
|
42
|
+
description: A scorer scored a run. `data` is a score.
|
|
43
|
+
messages:
|
|
44
|
+
ScoreRecorded:
|
|
45
|
+
$ref: '#/components/messages/ScoreRecorded'
|
|
46
|
+
pr-opened:
|
|
47
|
+
address: osf.pr.opened.v1
|
|
48
|
+
description: The factory opened a pull request.
|
|
49
|
+
messages:
|
|
50
|
+
PrOpened:
|
|
51
|
+
$ref: '#/components/messages/PrOpened'
|
|
52
|
+
operations:
|
|
53
|
+
publishWorkReceived:
|
|
54
|
+
action: send
|
|
55
|
+
summary: Published by intake.
|
|
56
|
+
channel:
|
|
57
|
+
$ref: '#/channels/work-received'
|
|
58
|
+
messages:
|
|
59
|
+
- $ref: '#/channels/work-received/messages/WorkReceived'
|
|
60
|
+
publishRunCompleted:
|
|
61
|
+
action: send
|
|
62
|
+
summary: Published by foreman.
|
|
63
|
+
channel:
|
|
64
|
+
$ref: '#/channels/run-completed'
|
|
65
|
+
messages:
|
|
66
|
+
- $ref: '#/channels/run-completed/messages/RunCompleted'
|
|
67
|
+
publishGateRequested:
|
|
68
|
+
action: send
|
|
69
|
+
summary: Published by gatekeeper.
|
|
70
|
+
channel:
|
|
71
|
+
$ref: '#/channels/gate-requested'
|
|
72
|
+
messages:
|
|
73
|
+
- $ref: '#/channels/gate-requested/messages/GateRequested'
|
|
74
|
+
publishGateDecided:
|
|
75
|
+
action: send
|
|
76
|
+
summary: Published by gatekeeper.
|
|
77
|
+
channel:
|
|
78
|
+
$ref: '#/channels/gate-decided'
|
|
79
|
+
messages:
|
|
80
|
+
- $ref: '#/channels/gate-decided/messages/GateDecided'
|
|
81
|
+
publishScoreRecorded:
|
|
82
|
+
action: send
|
|
83
|
+
summary: Published by scorekit.
|
|
84
|
+
channel:
|
|
85
|
+
$ref: '#/channels/score-recorded'
|
|
86
|
+
messages:
|
|
87
|
+
- $ref: '#/channels/score-recorded/messages/ScoreRecorded'
|
|
88
|
+
publishPrOpened:
|
|
89
|
+
action: send
|
|
90
|
+
summary: Published by forge adapters.
|
|
91
|
+
channel:
|
|
92
|
+
$ref: '#/channels/pr-opened'
|
|
93
|
+
messages:
|
|
94
|
+
- $ref: '#/channels/pr-opened/messages/PrOpened'
|
|
95
|
+
components:
|
|
96
|
+
messages:
|
|
97
|
+
WorkReceived:
|
|
98
|
+
name: work.received@v1
|
|
99
|
+
title: work.received@v1
|
|
100
|
+
contentType: application/cloudevents+json
|
|
101
|
+
payload:
|
|
102
|
+
schemaFormat: application/schema+json;version=draft-2020-12
|
|
103
|
+
schema:
|
|
104
|
+
$ref: './work-received/work-received.schema.json'
|
|
105
|
+
RunCompleted:
|
|
106
|
+
name: run.completed@v1
|
|
107
|
+
title: run.completed@v1
|
|
108
|
+
contentType: application/cloudevents+json
|
|
109
|
+
payload:
|
|
110
|
+
schemaFormat: application/schema+json;version=draft-2020-12
|
|
111
|
+
schema:
|
|
112
|
+
$ref: './run-completed/run-completed.schema.json'
|
|
113
|
+
GateRequested:
|
|
114
|
+
name: gate.requested@v1
|
|
115
|
+
title: gate.requested@v1
|
|
116
|
+
contentType: application/cloudevents+json
|
|
117
|
+
payload:
|
|
118
|
+
schemaFormat: application/schema+json;version=draft-2020-12
|
|
119
|
+
schema:
|
|
120
|
+
$ref: './gate-requested/gate-requested.schema.json'
|
|
121
|
+
GateDecided:
|
|
122
|
+
name: gate.decided@v1
|
|
123
|
+
title: gate.decided@v1
|
|
124
|
+
contentType: application/cloudevents+json
|
|
125
|
+
payload:
|
|
126
|
+
schemaFormat: application/schema+json;version=draft-2020-12
|
|
127
|
+
schema:
|
|
128
|
+
$ref: './gate-decided/gate-decided.schema.json'
|
|
129
|
+
ScoreRecorded:
|
|
130
|
+
name: score.recorded@v1
|
|
131
|
+
title: score.recorded@v1
|
|
132
|
+
contentType: application/cloudevents+json
|
|
133
|
+
payload:
|
|
134
|
+
schemaFormat: application/schema+json;version=draft-2020-12
|
|
135
|
+
schema:
|
|
136
|
+
$ref: './score-recorded/score-recorded.schema.json'
|
|
137
|
+
PrOpened:
|
|
138
|
+
name: pr.opened@v1
|
|
139
|
+
title: pr.opened@v1
|
|
140
|
+
contentType: application/cloudevents+json
|
|
141
|
+
payload:
|
|
142
|
+
schemaFormat: application/schema+json;version=draft-2020-12
|
|
143
|
+
schema:
|
|
144
|
+
$ref: './pr-opened/pr-opened.schema.json'
|
|
@@ -0,0 +1,122 @@
|
|
|
1
|
+
{
|
|
2
|
+
"$schema": "https://json-schema.org/draft/2020-12/schema",
|
|
3
|
+
"$id": "https://raw.githubusercontent.com/sesigl/open-software-factory/main/contracts/events/gate-decided/gate-decided.schema.json",
|
|
4
|
+
"title": "GateDecided",
|
|
5
|
+
"$comment": "Generated by `pnpm build` in contracts/ from typespec/. Do not edit.",
|
|
6
|
+
"type": "object",
|
|
7
|
+
"properties": {
|
|
8
|
+
"specversion": {
|
|
9
|
+
"type": "string",
|
|
10
|
+
"const": "1.0"
|
|
11
|
+
},
|
|
12
|
+
"id": {
|
|
13
|
+
"type": "string",
|
|
14
|
+
"minLength": 1,
|
|
15
|
+
"maxLength": 128,
|
|
16
|
+
"description": "Unique per source. A consumer that sees the same source and id twice handles the event once."
|
|
17
|
+
},
|
|
18
|
+
"source": {
|
|
19
|
+
"type": "string",
|
|
20
|
+
"minLength": 1,
|
|
21
|
+
"description": "The producing module, for example \"/osf/intake\"."
|
|
22
|
+
},
|
|
23
|
+
"type": {
|
|
24
|
+
"type": "string",
|
|
25
|
+
"const": "gate.decided@v1"
|
|
26
|
+
},
|
|
27
|
+
"time": {
|
|
28
|
+
"type": "string",
|
|
29
|
+
"format": "date-time"
|
|
30
|
+
},
|
|
31
|
+
"subject": {
|
|
32
|
+
"type": "string",
|
|
33
|
+
"description": "The ID of the thing the event is about: work item, gate, run, score or pull request."
|
|
34
|
+
},
|
|
35
|
+
"datacontenttype": {
|
|
36
|
+
"type": "string",
|
|
37
|
+
"const": "application/json"
|
|
38
|
+
},
|
|
39
|
+
"dataschema": {
|
|
40
|
+
"type": "string",
|
|
41
|
+
"format": "uri"
|
|
42
|
+
},
|
|
43
|
+
"traceparent": {
|
|
44
|
+
"type": "string",
|
|
45
|
+
"description": "W3C trace context (CloudEvents distributed tracing extension)."
|
|
46
|
+
},
|
|
47
|
+
"data": {
|
|
48
|
+
"type": "object",
|
|
49
|
+
"properties": {
|
|
50
|
+
"gateId": {
|
|
51
|
+
"type": "string",
|
|
52
|
+
"minLength": 1,
|
|
53
|
+
"maxLength": 128,
|
|
54
|
+
"description": "An identifier chosen by the producer. ULIDs are recommended; consumers treat it as opaque."
|
|
55
|
+
},
|
|
56
|
+
"workItemId": {
|
|
57
|
+
"type": "string",
|
|
58
|
+
"minLength": 1,
|
|
59
|
+
"maxLength": 128,
|
|
60
|
+
"description": "An identifier chosen by the producer. ULIDs are recommended; consumers treat it as opaque."
|
|
61
|
+
},
|
|
62
|
+
"decision": {
|
|
63
|
+
"type": "string",
|
|
64
|
+
"enum": [
|
|
65
|
+
"approved",
|
|
66
|
+
"rejected",
|
|
67
|
+
"expired"
|
|
68
|
+
]
|
|
69
|
+
},
|
|
70
|
+
"decidedBy": {
|
|
71
|
+
"type": "object",
|
|
72
|
+
"properties": {
|
|
73
|
+
"kind": {
|
|
74
|
+
"type": "string",
|
|
75
|
+
"enum": [
|
|
76
|
+
"person",
|
|
77
|
+
"trigger"
|
|
78
|
+
],
|
|
79
|
+
"description": "Who caused something: a person, or an automated trigger such as a cron rule or an alert."
|
|
80
|
+
},
|
|
81
|
+
"id": {
|
|
82
|
+
"type": "string",
|
|
83
|
+
"minLength": 1,
|
|
84
|
+
"description": "Stable ID in the source system, prefixed with the source, for example \"slack:U024BE7LH\"."
|
|
85
|
+
},
|
|
86
|
+
"displayName": {
|
|
87
|
+
"type": "string",
|
|
88
|
+
"description": "Shown to people, for example \"Varun\"."
|
|
89
|
+
}
|
|
90
|
+
},
|
|
91
|
+
"required": [
|
|
92
|
+
"kind",
|
|
93
|
+
"id"
|
|
94
|
+
],
|
|
95
|
+
"description": "The person who decided. Absent only when the gate expired."
|
|
96
|
+
},
|
|
97
|
+
"decidedAt": {
|
|
98
|
+
"type": "string",
|
|
99
|
+
"format": "date-time"
|
|
100
|
+
},
|
|
101
|
+
"reason": {
|
|
102
|
+
"type": "string"
|
|
103
|
+
}
|
|
104
|
+
},
|
|
105
|
+
"required": [
|
|
106
|
+
"gateId",
|
|
107
|
+
"workItemId",
|
|
108
|
+
"decision",
|
|
109
|
+
"decidedAt"
|
|
110
|
+
]
|
|
111
|
+
}
|
|
112
|
+
},
|
|
113
|
+
"required": [
|
|
114
|
+
"specversion",
|
|
115
|
+
"id",
|
|
116
|
+
"source",
|
|
117
|
+
"type",
|
|
118
|
+
"time",
|
|
119
|
+
"data"
|
|
120
|
+
],
|
|
121
|
+
"description": "gatekeeper: a gate was decided."
|
|
122
|
+
}
|
|
@@ -0,0 +1,135 @@
|
|
|
1
|
+
{
|
|
2
|
+
"$schema": "https://json-schema.org/draft/2020-12/schema",
|
|
3
|
+
"$id": "https://raw.githubusercontent.com/sesigl/open-software-factory/main/contracts/events/gate-requested/gate-requested.schema.json",
|
|
4
|
+
"title": "GateRequested",
|
|
5
|
+
"$comment": "Generated by `pnpm build` in contracts/ from typespec/. Do not edit.",
|
|
6
|
+
"type": "object",
|
|
7
|
+
"properties": {
|
|
8
|
+
"specversion": {
|
|
9
|
+
"type": "string",
|
|
10
|
+
"const": "1.0"
|
|
11
|
+
},
|
|
12
|
+
"id": {
|
|
13
|
+
"type": "string",
|
|
14
|
+
"minLength": 1,
|
|
15
|
+
"maxLength": 128,
|
|
16
|
+
"description": "Unique per source. A consumer that sees the same source and id twice handles the event once."
|
|
17
|
+
},
|
|
18
|
+
"source": {
|
|
19
|
+
"type": "string",
|
|
20
|
+
"minLength": 1,
|
|
21
|
+
"description": "The producing module, for example \"/osf/intake\"."
|
|
22
|
+
},
|
|
23
|
+
"type": {
|
|
24
|
+
"type": "string",
|
|
25
|
+
"const": "gate.requested@v1"
|
|
26
|
+
},
|
|
27
|
+
"time": {
|
|
28
|
+
"type": "string",
|
|
29
|
+
"format": "date-time"
|
|
30
|
+
},
|
|
31
|
+
"subject": {
|
|
32
|
+
"type": "string",
|
|
33
|
+
"description": "The ID of the thing the event is about: work item, gate, run, score or pull request."
|
|
34
|
+
},
|
|
35
|
+
"datacontenttype": {
|
|
36
|
+
"type": "string",
|
|
37
|
+
"const": "application/json"
|
|
38
|
+
},
|
|
39
|
+
"dataschema": {
|
|
40
|
+
"type": "string",
|
|
41
|
+
"format": "uri"
|
|
42
|
+
},
|
|
43
|
+
"traceparent": {
|
|
44
|
+
"type": "string",
|
|
45
|
+
"description": "W3C trace context (CloudEvents distributed tracing extension)."
|
|
46
|
+
},
|
|
47
|
+
"data": {
|
|
48
|
+
"type": "object",
|
|
49
|
+
"properties": {
|
|
50
|
+
"gateId": {
|
|
51
|
+
"type": "string",
|
|
52
|
+
"minLength": 1,
|
|
53
|
+
"maxLength": 128,
|
|
54
|
+
"description": "An identifier chosen by the producer. ULIDs are recommended; consumers treat it as opaque."
|
|
55
|
+
},
|
|
56
|
+
"workItemId": {
|
|
57
|
+
"type": "string",
|
|
58
|
+
"minLength": 1,
|
|
59
|
+
"maxLength": 128,
|
|
60
|
+
"description": "An identifier chosen by the producer. ULIDs are recommended; consumers treat it as opaque."
|
|
61
|
+
},
|
|
62
|
+
"runId": {
|
|
63
|
+
"type": "string",
|
|
64
|
+
"minLength": 1,
|
|
65
|
+
"maxLength": 128,
|
|
66
|
+
"description": "An identifier chosen by the producer. ULIDs are recommended; consumers treat it as opaque."
|
|
67
|
+
},
|
|
68
|
+
"stage": {
|
|
69
|
+
"type": "string",
|
|
70
|
+
"pattern": "^[a-z][a-z0-9-]{0,62}$",
|
|
71
|
+
"description": "The stage that waits."
|
|
72
|
+
},
|
|
73
|
+
"policy": {
|
|
74
|
+
"type": "string",
|
|
75
|
+
"minLength": 1,
|
|
76
|
+
"description": "Path to the policy in the factory definition."
|
|
77
|
+
},
|
|
78
|
+
"approvers": {
|
|
79
|
+
"type": "array",
|
|
80
|
+
"items": {
|
|
81
|
+
"type": "string",
|
|
82
|
+
"pattern": "^[a-z][a-z0-9-]{0,62}$",
|
|
83
|
+
"description": "A lowercase name: letters, digits and hyphens, starting with a letter. Agents, stages, adapters, sources."
|
|
84
|
+
},
|
|
85
|
+
"minItems": 1,
|
|
86
|
+
"description": "Approver groups picked by the policy."
|
|
87
|
+
},
|
|
88
|
+
"summary": {
|
|
89
|
+
"type": "string",
|
|
90
|
+
"minLength": 1,
|
|
91
|
+
"description": "What the approver is asked to decide, shown in the channel."
|
|
92
|
+
},
|
|
93
|
+
"links": {
|
|
94
|
+
"type": "array",
|
|
95
|
+
"items": {
|
|
96
|
+
"type": "object",
|
|
97
|
+
"properties": {
|
|
98
|
+
"url": {
|
|
99
|
+
"type": "string",
|
|
100
|
+
"format": "uri"
|
|
101
|
+
},
|
|
102
|
+
"title": {
|
|
103
|
+
"type": "string"
|
|
104
|
+
}
|
|
105
|
+
},
|
|
106
|
+
"required": [
|
|
107
|
+
"url"
|
|
108
|
+
]
|
|
109
|
+
}
|
|
110
|
+
},
|
|
111
|
+
"expiresAt": {
|
|
112
|
+
"type": "string",
|
|
113
|
+
"format": "date-time"
|
|
114
|
+
}
|
|
115
|
+
},
|
|
116
|
+
"required": [
|
|
117
|
+
"gateId",
|
|
118
|
+
"workItemId",
|
|
119
|
+
"stage",
|
|
120
|
+
"policy",
|
|
121
|
+
"approvers",
|
|
122
|
+
"summary"
|
|
123
|
+
]
|
|
124
|
+
}
|
|
125
|
+
},
|
|
126
|
+
"required": [
|
|
127
|
+
"specversion",
|
|
128
|
+
"id",
|
|
129
|
+
"source",
|
|
130
|
+
"type",
|
|
131
|
+
"time",
|
|
132
|
+
"data"
|
|
133
|
+
],
|
|
134
|
+
"description": "gatekeeper: a decision is needed before a stage starts."
|
|
135
|
+
}
|
|
@@ -0,0 +1,138 @@
|
|
|
1
|
+
{
|
|
2
|
+
"$schema": "https://json-schema.org/draft/2020-12/schema",
|
|
3
|
+
"$id": "https://raw.githubusercontent.com/sesigl/open-software-factory/main/contracts/events/pr-opened/pr-opened.schema.json",
|
|
4
|
+
"title": "PrOpened",
|
|
5
|
+
"$comment": "Generated by `pnpm build` in contracts/ from typespec/. Do not edit.",
|
|
6
|
+
"type": "object",
|
|
7
|
+
"properties": {
|
|
8
|
+
"specversion": {
|
|
9
|
+
"type": "string",
|
|
10
|
+
"const": "1.0"
|
|
11
|
+
},
|
|
12
|
+
"id": {
|
|
13
|
+
"type": "string",
|
|
14
|
+
"minLength": 1,
|
|
15
|
+
"maxLength": 128,
|
|
16
|
+
"description": "Unique per source. A consumer that sees the same source and id twice handles the event once."
|
|
17
|
+
},
|
|
18
|
+
"source": {
|
|
19
|
+
"type": "string",
|
|
20
|
+
"minLength": 1,
|
|
21
|
+
"description": "The producing module, for example \"/osf/intake\"."
|
|
22
|
+
},
|
|
23
|
+
"type": {
|
|
24
|
+
"type": "string",
|
|
25
|
+
"const": "pr.opened@v1"
|
|
26
|
+
},
|
|
27
|
+
"time": {
|
|
28
|
+
"type": "string",
|
|
29
|
+
"format": "date-time"
|
|
30
|
+
},
|
|
31
|
+
"subject": {
|
|
32
|
+
"type": "string",
|
|
33
|
+
"description": "The ID of the thing the event is about: work item, gate, run, score or pull request."
|
|
34
|
+
},
|
|
35
|
+
"datacontenttype": {
|
|
36
|
+
"type": "string",
|
|
37
|
+
"const": "application/json"
|
|
38
|
+
},
|
|
39
|
+
"dataschema": {
|
|
40
|
+
"type": "string",
|
|
41
|
+
"format": "uri"
|
|
42
|
+
},
|
|
43
|
+
"traceparent": {
|
|
44
|
+
"type": "string",
|
|
45
|
+
"description": "W3C trace context (CloudEvents distributed tracing extension)."
|
|
46
|
+
},
|
|
47
|
+
"data": {
|
|
48
|
+
"type": "object",
|
|
49
|
+
"properties": {
|
|
50
|
+
"url": {
|
|
51
|
+
"type": "string",
|
|
52
|
+
"format": "uri"
|
|
53
|
+
},
|
|
54
|
+
"repository": {
|
|
55
|
+
"type": "string",
|
|
56
|
+
"format": "uri"
|
|
57
|
+
},
|
|
58
|
+
"number": {
|
|
59
|
+
"type": "integer",
|
|
60
|
+
"minimum": 1
|
|
61
|
+
},
|
|
62
|
+
"title": {
|
|
63
|
+
"type": "string",
|
|
64
|
+
"minLength": 1
|
|
65
|
+
},
|
|
66
|
+
"headBranch": {
|
|
67
|
+
"type": "string"
|
|
68
|
+
},
|
|
69
|
+
"baseBranch": {
|
|
70
|
+
"type": "string"
|
|
71
|
+
},
|
|
72
|
+
"headCommit": {
|
|
73
|
+
"type": "string",
|
|
74
|
+
"pattern": "^[0-9a-f]{7,40}$",
|
|
75
|
+
"description": "A Git commit hash, abbreviated (7+) or full. The factory version is the commit of the factory definition."
|
|
76
|
+
},
|
|
77
|
+
"author": {
|
|
78
|
+
"type": "object",
|
|
79
|
+
"properties": {
|
|
80
|
+
"kind": {
|
|
81
|
+
"type": "string",
|
|
82
|
+
"enum": [
|
|
83
|
+
"person",
|
|
84
|
+
"trigger"
|
|
85
|
+
],
|
|
86
|
+
"description": "Who caused something: a person, or an automated trigger such as a cron rule or an alert."
|
|
87
|
+
},
|
|
88
|
+
"id": {
|
|
89
|
+
"type": "string",
|
|
90
|
+
"minLength": 1,
|
|
91
|
+
"description": "Stable ID in the source system, prefixed with the source, for example \"slack:U024BE7LH\"."
|
|
92
|
+
},
|
|
93
|
+
"displayName": {
|
|
94
|
+
"type": "string",
|
|
95
|
+
"description": "Shown to people, for example \"Varun\"."
|
|
96
|
+
}
|
|
97
|
+
},
|
|
98
|
+
"required": [
|
|
99
|
+
"kind",
|
|
100
|
+
"id"
|
|
101
|
+
],
|
|
102
|
+
"description": "A person or trigger. RUNTIME-008: every run is attributed to one."
|
|
103
|
+
},
|
|
104
|
+
"workItemId": {
|
|
105
|
+
"type": "string",
|
|
106
|
+
"minLength": 1,
|
|
107
|
+
"maxLength": 128,
|
|
108
|
+
"description": "An identifier chosen by the producer. ULIDs are recommended; consumers treat it as opaque."
|
|
109
|
+
},
|
|
110
|
+
"runId": {
|
|
111
|
+
"type": "string",
|
|
112
|
+
"minLength": 1,
|
|
113
|
+
"maxLength": 128,
|
|
114
|
+
"description": "An identifier chosen by the producer. ULIDs are recommended; consumers treat it as opaque."
|
|
115
|
+
}
|
|
116
|
+
},
|
|
117
|
+
"required": [
|
|
118
|
+
"url",
|
|
119
|
+
"repository",
|
|
120
|
+
"number",
|
|
121
|
+
"title",
|
|
122
|
+
"headBranch",
|
|
123
|
+
"baseBranch",
|
|
124
|
+
"headCommit",
|
|
125
|
+
"author"
|
|
126
|
+
]
|
|
127
|
+
}
|
|
128
|
+
},
|
|
129
|
+
"required": [
|
|
130
|
+
"specversion",
|
|
131
|
+
"id",
|
|
132
|
+
"source",
|
|
133
|
+
"type",
|
|
134
|
+
"time",
|
|
135
|
+
"data"
|
|
136
|
+
],
|
|
137
|
+
"description": "forge adapters: the factory opened a pull request."
|
|
138
|
+
}
|