@kungfu-tech/buildchain 3.0.6 → 3.0.7-alpha.1
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/README.md +4 -2
- package/actions/promote-buildchain-ref/README.md +10 -0
- package/actions/release-tail/README.md +17 -0
- package/bin/buildchain.mjs +11 -0
- package/bin/internal/command-registry.mjs +2 -0
- package/contracts/auditable-demo-scenario-v1.schema.json +1 -1
- package/contracts/engineering-housekeeper-v1.schema.json +143 -0
- package/contracts/fixtures/engineering-housekeeper-v1/cases.json +68 -0
- package/contracts/fixtures/release-tail-capabilities-v1/kungfu-alpha.json +273 -0
- package/contracts/publication-rehearsal-capsule-v1.schema.json +173 -0
- package/contracts/release-tail-capabilities-v1.schema.json +199 -0
- package/contracts/release-tail-provider-bindings-v1.schema.json +56 -0
- package/dist/site/agent-index.json +3 -0
- package/dist/site/artifact-schemas.json +6 -0
- package/dist/site/buildchain-contract.json +42 -27
- package/dist/site/buildchain-site.json +384 -33
- package/dist/site/capability-registry.json +15 -12
- package/dist/site/cli-registry.json +102 -0
- package/dist/site/controller-registry.json +19 -3
- package/dist/site/kfd-claims.json +359 -17
- package/dist/site/kfd-upstream-aggregate.json +1 -1
- package/dist/site/manual-registry.json +53 -7
- package/dist/site/node-api-registry.json +5882 -3605
- package/dist/site/page-registry.json +348 -21
- package/dist/site/public-surface-audit.json +396 -16
- package/dist/site/publication-authority-registry.json +106 -1
- package/dist/site/publication-registry.json +4 -4
- package/dist/site/release-provenance.json +7 -0
- package/dist/site/schemas/publication-rehearsal-capsule-v1.schema.json +269 -0
- package/dist/site/schemas/release-tail-capabilities-v1.schema.json +353 -0
- package/dist/site/schemas/release-tail-provider-bindings-v1.schema.json +94 -0
- package/dist/site/site-manifest.json +35 -11
- package/dist/site/workflow-registry.json +204 -10
- package/docs/MAP.md +6 -2
- package/docs/auditable-demo.md +2 -2
- package/docs/cli-reference.md +136 -0
- package/docs/dev-delivery-warrant.md +49 -4
- package/docs/engineering-housekeeper.md +138 -0
- package/docs/lifecycle-protocol.md +4 -2
- package/docs/node-api-reference.md +495 -281
- package/docs/publication-rehearsal.md +94 -0
- package/docs/release-governance.md +17 -2
- package/docs/release-tail-contract.md +160 -0
- package/docs/release-tail-provider-plane.md +120 -0
- package/docs/reusable-build-surface.md +11 -0
- package/package.json +11 -3
- package/packages/core/artifact-signing.js +61 -0
- package/packages/core/buildchain-agent-manuals.js +3 -0
- package/packages/core/buildchain-config.js +66 -6
- package/packages/core/buildchain-kfd-claims.js +1 -1
- package/packages/core/buildchain-publication-authority.js +5 -0
- package/packages/core/controller-evidence.js +2 -1
- package/packages/core/dev-delivery-warrant-cancellation.js +1 -0
- package/packages/core/dev-delivery-warrant-shadow.js +502 -0
- package/packages/core/dev-delivery-warrant.js +15 -6
- package/packages/core/diagnostics.js +8 -3
- package/packages/core/engineering-housekeeper-github-client.js +222 -0
- package/packages/core/engineering-housekeeper-github.js +501 -0
- package/packages/core/engineering-housekeeper.js +259 -0
- package/packages/core/index.js +42 -0
- package/packages/core/kfd-gate.js +45 -15
- package/packages/core/paper-agent-entry.js +11 -5
- package/packages/core/paper-repository.js +1 -0
- package/packages/core/paper-scaffold-content.js +21 -0
- package/packages/core/paper.js +28 -2
- package/packages/core/publication-rehearsal-projection.js +173 -0
- package/packages/core/publication-rehearsal-runtime.js +921 -0
- package/packages/core/release-passport.js +130 -20
- package/packages/core/release-tail-compatibility.js +60 -0
- package/packages/core/release-tail-provider-adapters.js +461 -0
- package/packages/core/release-tail-provider-plane.js +1228 -0
- package/scripts/assemble-publication-artifact-admission.mjs +1 -1
- package/scripts/assemble-self-publication-admission.mjs +2 -2
- package/scripts/audit-publication-control-plane.mjs +1 -1
- package/scripts/auditable-demo-bundle-verification.mjs +2 -3
- package/scripts/auditable-demo-platform.mjs +2 -2
- package/scripts/auditable-demo-renditions.mjs +1 -1
- package/scripts/auditable-demo.mjs +2 -2
- package/scripts/build-contract-core.mjs +8 -3
- package/scripts/build-standalone-binary.mjs +14 -3
- package/scripts/buildchain-cli-help.mjs +13 -0
- package/scripts/check-core-mechanism-inventory.mjs +347 -0
- package/scripts/check-inventory.mjs +10 -8
- package/scripts/check-maintainability.mjs +9 -2
- package/scripts/check-release-tail-contract.mjs +435 -0
- package/scripts/dev-delivery-warrant.mjs +31 -4
- package/scripts/dev-pr-auto-merge.mjs +30 -4
- package/scripts/dev-pr-delivery-warrant.mjs +50 -0
- package/scripts/engineering-housekeeper-workflow.mjs +394 -0
- package/scripts/generate-channel-promotion-workflow.mjs +10 -8
- package/scripts/generate-site-bundle.mjs +47 -4
- package/scripts/init-repo.mjs +26 -2
- package/scripts/inspect-artifact-signing-requests.mjs +6 -0
- package/scripts/materialize-self-release-candidate-version.mjs +137 -0
- package/scripts/publication-commit-evidence.mjs +69 -23
- package/scripts/release-candidate-resolver.mjs +16 -10
- package/scripts/release-tail.mjs +159 -0
- package/scripts/resume-from-candidate-run.mjs +123 -9
- package/scripts/seal-artifact-signing-requests.mjs +6 -0
- package/scripts/site-capability-metadata.mjs +13 -0
- package/scripts/v4-architecture.mjs +600 -0
- package/scripts/web-surface-core.mjs +8 -2
- package/scripts/workflow-call-contract.mjs +184 -5
|
@@ -69,6 +69,7 @@ function normalizeCandidate(input, expected) {
|
|
|
69
69
|
closureRoot: exactRoot(input.closureRoot, "closureRoot"),
|
|
70
70
|
dependencyRoot: exactRoot(input.dependencyRoot, "dependencyRoot"),
|
|
71
71
|
toolchainRoot: exactRoot(input.toolchainRoot, "toolchainRoot"),
|
|
72
|
+
...(Object.hasOwn(input, "sourceWorkflowRunId") ? { sourceWorkflowRunId: nonNegativeInteger(input.sourceWorkflowRunId, "candidate sourceWorkflowRunId", 0) } : {}),
|
|
72
73
|
priority: priority(input.priority),
|
|
73
74
|
enqueuedAt: timestamp(input.enqueuedAt, "candidate enqueuedAt"),
|
|
74
75
|
updatedAt: timestamp(input.updatedAt || input.enqueuedAt, "candidate updatedAt"),
|
|
@@ -178,6 +179,7 @@ function submissionReceipt({ before, after, candidate, action, now }) {
|
|
|
178
179
|
closureRoot: candidate.closureRoot,
|
|
179
180
|
dependencyRoot: candidate.dependencyRoot,
|
|
180
181
|
toolchainRoot: candidate.toolchainRoot,
|
|
182
|
+
sourceWorkflowRunId: candidate.sourceWorkflowRunId,
|
|
181
183
|
deliveryClass: candidate.deliveryClass,
|
|
182
184
|
priority: candidate.priority,
|
|
183
185
|
retainedEnqueuedAt: candidate.enqueuedAt,
|
|
@@ -223,7 +225,12 @@ export function submitDevDeliveryCandidate(queueInput, input, { now = new Date()
|
|
|
223
225
|
selected = existing;
|
|
224
226
|
} else {
|
|
225
227
|
if (before.activeWarrant?.candidateId === existing.candidateId) {
|
|
226
|
-
|
|
228
|
+
if (existing.sourceHead !== attemptedCandidate.sourceHead) {
|
|
229
|
+
throw new Error("selected candidate sourceHead cannot change before terminal Warrant closeout");
|
|
230
|
+
}
|
|
231
|
+
action = "active-warrant-retained-noop";
|
|
232
|
+
selected = existing;
|
|
233
|
+
return { candidate: selected, action };
|
|
227
234
|
}
|
|
228
235
|
const headChanged = existing.sourceHead !== attemptedCandidate.sourceHead;
|
|
229
236
|
existing.sourceHead = attemptedCandidate.sourceHead;
|
|
@@ -309,7 +316,7 @@ export function selectDevDeliveryWarrant(queueInput, { now = new Date().toISOStr
|
|
|
309
316
|
candidateId: queue.activeWarrant.candidateId,
|
|
310
317
|
fencingToken: queue.activeWarrant.fencingToken,
|
|
311
318
|
leaseGeneration: queue.activeWarrant.generation,
|
|
312
|
-
expectedOldStateRoot: queue.stateRoot,
|
|
319
|
+
expectedOldStateRoot: recoveryReceipt?.expectedOldStateRoot || queue.stateRoot,
|
|
313
320
|
nextStateRoot: queue.stateRoot,
|
|
314
321
|
nextAction: "Continue the active delivery attempt; later candidates remain visibly queued.",
|
|
315
322
|
};
|
|
@@ -363,6 +370,7 @@ export function selectDevDeliveryWarrant(queueInput, { now = new Date().toISOStr
|
|
|
363
370
|
closureRoot: candidate.closureRoot,
|
|
364
371
|
dependencyRoot: candidate.dependencyRoot,
|
|
365
372
|
toolchainRoot: candidate.toolchainRoot,
|
|
373
|
+
sourceWorkflowRunId: candidate.sourceWorkflowRunId,
|
|
366
374
|
deliveryClass: candidate.deliveryClass,
|
|
367
375
|
generation: next.fencingCounter,
|
|
368
376
|
expectedOldStateRoot: before.stateRoot,
|
|
@@ -397,6 +405,7 @@ export function selectDevDeliveryWarrant(queueInput, { now = new Date().toISOStr
|
|
|
397
405
|
closureRoot: transaction.result.candidate.closureRoot,
|
|
398
406
|
dependencyRoot: transaction.result.candidate.dependencyRoot,
|
|
399
407
|
toolchainRoot: transaction.result.candidate.toolchainRoot,
|
|
408
|
+
sourceWorkflowRunId: transaction.result.candidate.sourceWorkflowRunId,
|
|
400
409
|
deliveryClass: transaction.result.candidate.deliveryClass,
|
|
401
410
|
queueAgeSeconds: selected.priority.ageSeconds,
|
|
402
411
|
basePriority: selected.priority.basePriority,
|
|
@@ -404,7 +413,7 @@ export function selectDevDeliveryWarrant(queueInput, { now = new Date().toISOStr
|
|
|
404
413
|
effectivePriority: selected.priority.score,
|
|
405
414
|
fencingToken: transaction.result.warrant.fencingToken,
|
|
406
415
|
leaseGeneration: transaction.result.warrant.generation,
|
|
407
|
-
expectedOldStateRoot: transaction.expectedOldStateRoot,
|
|
416
|
+
expectedOldStateRoot: recoveryReceipt?.expectedOldStateRoot || transaction.expectedOldStateRoot,
|
|
408
417
|
nextStateRoot: transaction.after.stateRoot,
|
|
409
418
|
nextAction: transaction.result.warrant.nextAction,
|
|
410
419
|
};
|
|
@@ -417,12 +426,12 @@ export function selectDevDeliveryWarrant(queueInput, { now = new Date().toISOStr
|
|
|
417
426
|
};
|
|
418
427
|
}
|
|
419
428
|
|
|
420
|
-
function assertWarrantMutation(queue, warrant, now) {
|
|
429
|
+
function assertWarrantMutation(queue, warrant, now, { allowExpired = false } = {}) {
|
|
421
430
|
if (!queue.activeWarrant) throw new Error("no active Delivery Warrant");
|
|
422
431
|
if (text(warrant?.fencingToken) !== queue.activeWarrant.fencingToken) throw new Error("stale fencing token");
|
|
423
432
|
if (Number(warrant?.generation) !== queue.activeWarrant.generation) throw new Error("stale lease generation");
|
|
424
433
|
if (text(warrant?.candidateId) !== queue.activeWarrant.candidateId) throw new Error("Warrant candidate mismatch");
|
|
425
|
-
if (Date.parse(queue.activeWarrant.expiresAt) <= Date.parse(now)) throw new Error("Delivery Warrant lease expired");
|
|
434
|
+
if (!allowExpired && Date.parse(queue.activeWarrant.expiresAt) <= Date.parse(now)) throw new Error("Delivery Warrant lease expired");
|
|
426
435
|
}
|
|
427
436
|
|
|
428
437
|
export function heartbeatDevDeliveryWarrant(queueInput, warrant, { now = new Date().toISOString(), leaseSeconds } = {}) {
|
|
@@ -517,7 +526,7 @@ export function closeDevDeliveryWarrant(queueInput, warrant, { outcome, evidence
|
|
|
517
526
|
const transaction = transition(
|
|
518
527
|
queueInput,
|
|
519
528
|
(queue, before) => {
|
|
520
|
-
assertWarrantMutation(before, warrant, currentTime);
|
|
529
|
+
assertWarrantMutation(before, warrant, currentTime, { allowExpired: true });
|
|
521
530
|
const active = clone(queue.activeWarrant);
|
|
522
531
|
const candidate = queue.candidates.find((entry) => entry.candidateId === active.candidateId);
|
|
523
532
|
candidate.status = normalizedOutcome;
|
|
@@ -84,10 +84,14 @@ function fileStats(cwd, entries = []) {
|
|
|
84
84
|
});
|
|
85
85
|
}
|
|
86
86
|
|
|
87
|
-
|
|
87
|
+
const TOOL_VERSION_PROBE_CWD = path.parse(process.execPath).root;
|
|
88
|
+
|
|
89
|
+
function commandVersion(command, args = ["--version"]) {
|
|
88
90
|
try {
|
|
89
91
|
return execFileSync(command, args, {
|
|
90
|
-
|
|
92
|
+
// Package managers may walk every parent even for --version. Keep this
|
|
93
|
+
// bounded instead of scanning an arbitrary consumer or shared temp root.
|
|
94
|
+
cwd: TOOL_VERSION_PROBE_CWD,
|
|
91
95
|
encoding: "utf8",
|
|
92
96
|
stdio: ["ignore", "pipe", "ignore"],
|
|
93
97
|
timeout: 5000,
|
|
@@ -493,7 +497,8 @@ export function collectRunnerDiagnostics() {
|
|
|
493
497
|
}
|
|
494
498
|
|
|
495
499
|
export function collectToolDiagnostics({ cwd = process.cwd(), tools = ["node", "pnpm", "npm", "git", "cmake", "ninja", "ccache", "sccache"] } = {}) {
|
|
496
|
-
|
|
500
|
+
void cwd;
|
|
501
|
+
return Object.fromEntries(tools.map((tool) => [tool, { version: commandVersion(tool) }]));
|
|
497
502
|
}
|
|
498
503
|
|
|
499
504
|
function parseJsonDiagnostics(value = "") {
|
|
@@ -0,0 +1,222 @@
|
|
|
1
|
+
function requiredString(value, field) {
|
|
2
|
+
const normalized = String(value || "").trim();
|
|
3
|
+
if (!normalized) throw new Error(`${field} is required`);
|
|
4
|
+
return normalized;
|
|
5
|
+
}
|
|
6
|
+
|
|
7
|
+
function normalizeRepository(value) {
|
|
8
|
+
const normalized = requiredString(value?.fullName || value, "repository");
|
|
9
|
+
const match = normalized.match(/^([^/\s]+)\/([^/\s]+)$/);
|
|
10
|
+
if (!match) throw new Error(`repository must be owner/repo, got: ${normalized}`);
|
|
11
|
+
return { owner: match[1], repo: match[2], fullName: normalized };
|
|
12
|
+
}
|
|
13
|
+
|
|
14
|
+
function refPath(name) {
|
|
15
|
+
return String(name)
|
|
16
|
+
.split("/")
|
|
17
|
+
.map((part) => encodeURIComponent(part))
|
|
18
|
+
.join("/");
|
|
19
|
+
}
|
|
20
|
+
|
|
21
|
+
function parseLinkHeader(value) {
|
|
22
|
+
const links = {};
|
|
23
|
+
for (const part of String(value || "").split(",")) {
|
|
24
|
+
const match = part.match(/<([^>]+)>;\s*rel="([^"]+)"/);
|
|
25
|
+
if (match) links[match[2]] = match[1];
|
|
26
|
+
}
|
|
27
|
+
return links;
|
|
28
|
+
}
|
|
29
|
+
|
|
30
|
+
function branchHeadOid(branch) {
|
|
31
|
+
return String(
|
|
32
|
+
branch?.commit?.sha || branch?.object?.sha || branch?.headOid || "",
|
|
33
|
+
).toLowerCase();
|
|
34
|
+
}
|
|
35
|
+
|
|
36
|
+
export class GitHubHousekeeperProviderError extends Error {
|
|
37
|
+
constructor(message, { operation = "github", status = 0, cause } = {}) {
|
|
38
|
+
super(message, { cause });
|
|
39
|
+
this.name = "GitHubHousekeeperProviderError";
|
|
40
|
+
this.operation = operation;
|
|
41
|
+
this.status = status;
|
|
42
|
+
}
|
|
43
|
+
}
|
|
44
|
+
|
|
45
|
+
function providerError(error, operation) {
|
|
46
|
+
if (error instanceof GitHubHousekeeperProviderError) return error;
|
|
47
|
+
return new GitHubHousekeeperProviderError(
|
|
48
|
+
`${operation} failed: ${error?.message || String(error)}`,
|
|
49
|
+
{ operation, cause: error },
|
|
50
|
+
);
|
|
51
|
+
}
|
|
52
|
+
|
|
53
|
+
export class GitHubHousekeeperClient {
|
|
54
|
+
constructor({
|
|
55
|
+
token,
|
|
56
|
+
apiUrl = "https://api.github.com",
|
|
57
|
+
fetchImpl = globalThis.fetch,
|
|
58
|
+
} = {}) {
|
|
59
|
+
if (typeof fetchImpl !== "function") throw new Error("fetch is required");
|
|
60
|
+
this.token = requiredString(token, "GitHub token");
|
|
61
|
+
this.apiUrl = String(apiUrl).replace(/\/+$/, "");
|
|
62
|
+
this.fetch = fetchImpl;
|
|
63
|
+
}
|
|
64
|
+
|
|
65
|
+
async request(method, requestPath, { body } = {}) {
|
|
66
|
+
const url = requestPath.startsWith("http")
|
|
67
|
+
? requestPath
|
|
68
|
+
: `${this.apiUrl}${requestPath}`;
|
|
69
|
+
if (new URL(url).origin !== new URL(this.apiUrl).origin) {
|
|
70
|
+
throw new GitHubHousekeeperProviderError(
|
|
71
|
+
`GitHub pagination cannot leave API origin: ${new URL(url).origin}`,
|
|
72
|
+
{ operation: `${method} ${requestPath}` },
|
|
73
|
+
);
|
|
74
|
+
}
|
|
75
|
+
let response;
|
|
76
|
+
try {
|
|
77
|
+
response = await this.fetch(url, {
|
|
78
|
+
method,
|
|
79
|
+
headers: {
|
|
80
|
+
accept: "application/vnd.github+json",
|
|
81
|
+
authorization: `Bearer ${this.token}`,
|
|
82
|
+
"content-type": "application/json",
|
|
83
|
+
"x-github-api-version": "2022-11-28",
|
|
84
|
+
},
|
|
85
|
+
body: body === undefined ? undefined : JSON.stringify(body),
|
|
86
|
+
});
|
|
87
|
+
} catch (error) {
|
|
88
|
+
throw providerError(error, `${method} ${requestPath}`);
|
|
89
|
+
}
|
|
90
|
+
const text = await response.text();
|
|
91
|
+
let data = null;
|
|
92
|
+
try {
|
|
93
|
+
data = text ? JSON.parse(text) : null;
|
|
94
|
+
} catch (error) {
|
|
95
|
+
throw new GitHubHousekeeperProviderError(
|
|
96
|
+
`${method} ${requestPath} returned invalid JSON`,
|
|
97
|
+
{
|
|
98
|
+
operation: `${method} ${requestPath}`,
|
|
99
|
+
status: response.status,
|
|
100
|
+
cause: error,
|
|
101
|
+
},
|
|
102
|
+
);
|
|
103
|
+
}
|
|
104
|
+
if (!response.ok) {
|
|
105
|
+
throw new GitHubHousekeeperProviderError(
|
|
106
|
+
`${method} ${requestPath} failed with ${response.status}: ${data?.message || text}`,
|
|
107
|
+
{ operation: `${method} ${requestPath}`, status: response.status },
|
|
108
|
+
);
|
|
109
|
+
}
|
|
110
|
+
return { data, response };
|
|
111
|
+
}
|
|
112
|
+
|
|
113
|
+
async paginate(requestPath, { maxPages = 1000 } = {}) {
|
|
114
|
+
const items = [];
|
|
115
|
+
const visited = new Set();
|
|
116
|
+
let next = requestPath;
|
|
117
|
+
while (next) {
|
|
118
|
+
if (visited.has(next)) {
|
|
119
|
+
throw new GitHubHousekeeperProviderError(
|
|
120
|
+
"GitHub pagination cycle detected",
|
|
121
|
+
{ operation: `GET ${requestPath}` },
|
|
122
|
+
);
|
|
123
|
+
}
|
|
124
|
+
if (visited.size >= maxPages) {
|
|
125
|
+
throw new GitHubHousekeeperProviderError(
|
|
126
|
+
`GitHub pagination exceeded ${maxPages} pages`,
|
|
127
|
+
{ operation: `GET ${requestPath}` },
|
|
128
|
+
);
|
|
129
|
+
}
|
|
130
|
+
visited.add(next);
|
|
131
|
+
const { data, response } = await this.request("GET", next);
|
|
132
|
+
if (!Array.isArray(data)) {
|
|
133
|
+
throw new GitHubHousekeeperProviderError(
|
|
134
|
+
"GitHub paginated response must be an array",
|
|
135
|
+
{ operation: `GET ${next}` },
|
|
136
|
+
);
|
|
137
|
+
}
|
|
138
|
+
items.push(...data);
|
|
139
|
+
next = parseLinkHeader(response.headers.get("link")).next || "";
|
|
140
|
+
}
|
|
141
|
+
return items;
|
|
142
|
+
}
|
|
143
|
+
|
|
144
|
+
async getRepository(repository) {
|
|
145
|
+
const coordinate = normalizeRepository(repository);
|
|
146
|
+
return (
|
|
147
|
+
await this.request("GET", `/repos/${coordinate.owner}/${coordinate.repo}`)
|
|
148
|
+
).data;
|
|
149
|
+
}
|
|
150
|
+
|
|
151
|
+
async listBranches(repository) {
|
|
152
|
+
const coordinate = normalizeRepository(repository);
|
|
153
|
+
return this.paginate(
|
|
154
|
+
`/repos/${coordinate.owner}/${coordinate.repo}/branches?per_page=100`,
|
|
155
|
+
);
|
|
156
|
+
}
|
|
157
|
+
|
|
158
|
+
async listOpenPullRequests(repository) {
|
|
159
|
+
const coordinate = normalizeRepository(repository);
|
|
160
|
+
return this.paginate(
|
|
161
|
+
`/repos/${coordinate.owner}/${coordinate.repo}/pulls?state=open&sort=updated&direction=asc&per_page=100`,
|
|
162
|
+
);
|
|
163
|
+
}
|
|
164
|
+
|
|
165
|
+
async getBranch(repository, name) {
|
|
166
|
+
const coordinate = normalizeRepository(repository);
|
|
167
|
+
return (
|
|
168
|
+
await this.request(
|
|
169
|
+
"GET",
|
|
170
|
+
`/repos/${coordinate.owner}/${coordinate.repo}/branches/${refPath(name)}`,
|
|
171
|
+
)
|
|
172
|
+
).data;
|
|
173
|
+
}
|
|
174
|
+
|
|
175
|
+
async getPullRequest(repository, number) {
|
|
176
|
+
const coordinate = normalizeRepository(repository);
|
|
177
|
+
return (
|
|
178
|
+
await this.request(
|
|
179
|
+
"GET",
|
|
180
|
+
`/repos/${coordinate.owner}/${coordinate.repo}/pulls/${number}`,
|
|
181
|
+
)
|
|
182
|
+
).data;
|
|
183
|
+
}
|
|
184
|
+
|
|
185
|
+
async compareCommits(repository, baseOid, headOid) {
|
|
186
|
+
const coordinate = normalizeRepository(repository);
|
|
187
|
+
return (
|
|
188
|
+
await this.request(
|
|
189
|
+
"GET",
|
|
190
|
+
`/repos/${coordinate.owner}/${coordinate.repo}/compare/${baseOid}...${headOid}`,
|
|
191
|
+
)
|
|
192
|
+
).data;
|
|
193
|
+
}
|
|
194
|
+
|
|
195
|
+
async deleteBranch(repository, name, { expectedHeadOid } = {}) {
|
|
196
|
+
const coordinate = normalizeRepository(repository);
|
|
197
|
+
const currentHeadOid = branchHeadOid(
|
|
198
|
+
await this.getBranch(coordinate.fullName, name),
|
|
199
|
+
);
|
|
200
|
+
if (!expectedHeadOid || currentHeadOid !== expectedHeadOid) {
|
|
201
|
+
throw new GitHubHousekeeperProviderError(
|
|
202
|
+
`branch ${name} changed before delete`,
|
|
203
|
+
{ operation: `delete-ref ${name}`, status: 409 },
|
|
204
|
+
);
|
|
205
|
+
}
|
|
206
|
+
await this.request(
|
|
207
|
+
"DELETE",
|
|
208
|
+
`/repos/${coordinate.owner}/${coordinate.repo}/git/refs/heads/${refPath(name)}`,
|
|
209
|
+
);
|
|
210
|
+
}
|
|
211
|
+
|
|
212
|
+
async addLabels(repository, number, labels) {
|
|
213
|
+
const coordinate = normalizeRepository(repository);
|
|
214
|
+
return (
|
|
215
|
+
await this.request(
|
|
216
|
+
"POST",
|
|
217
|
+
`/repos/${coordinate.owner}/${coordinate.repo}/issues/${number}/labels`,
|
|
218
|
+
{ body: { labels } },
|
|
219
|
+
)
|
|
220
|
+
).data;
|
|
221
|
+
}
|
|
222
|
+
}
|