paperclip-github-plugin 0.6.0 → 0.6.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 +2 -0
- package/dist/manifest.js +1 -1
- package/dist/worker.js +300 -21
- package/package.json +2 -2
package/README.md
CHANGED
|
@@ -78,6 +78,8 @@ Each mapped project can expose a **Pull Requests** entry in the sidebar that ope
|
|
|
78
78
|
|
|
79
79
|
Paperclip issue linkage on the queue prefers the GitHub issue that the pull request closes, so imported GitHub issues and delivery work stay connected in the same project view. If a pull request has no closing-issue-backed link yet, the queue falls back to the Paperclip issue created directly from that pull request and updates the table immediately when that create action returns.
|
|
80
80
|
|
|
81
|
+
Those pull-request-created Paperclip issues also stay in the scheduled/manual sync loop even when the pull request does not close a GitHub issue. GitHub Sync checks their CI, merge state, and review threads so new failures or unresolved feedback move the Paperclip issue back into active work.
|
|
82
|
+
|
|
81
83
|
The issue detail panel and sync-created comment annotations also preserve cross-repository linked pull requests, showing those PRs with their real repository path so operators land in the right place on GitHub.
|
|
82
84
|
|
|
83
85
|
### Agent workflows built in
|
package/dist/manifest.js
CHANGED
|
@@ -516,7 +516,7 @@ var require2 = createRequire(import.meta.url);
|
|
|
516
516
|
var packageJson = require2("../package.json");
|
|
517
517
|
var DASHBOARD_WIDGET_CAPABILITY = "ui.dashboardWidget.register";
|
|
518
518
|
var SCHEDULE_TICK_CRON = "* * * * *";
|
|
519
|
-
var MANIFEST_VERSION = "0.6.
|
|
519
|
+
var MANIFEST_VERSION = "0.6.1"?.trim() || typeof packageJson.version === "string" && packageJson.version.trim() || process.env.npm_package_version?.trim() || "0.0.0-dev";
|
|
520
520
|
var manifest = {
|
|
521
521
|
id: GITHUB_SYNC_PLUGIN_ID,
|
|
522
522
|
apiVersion: 1,
|
package/dist/worker.js
CHANGED
|
@@ -4262,6 +4262,12 @@ function countImportedIssuesForMappings(importRegistry, mappings) {
|
|
|
4262
4262
|
function buildTrackedIssueProgressKey(mapping, githubIssueId) {
|
|
4263
4263
|
return `${mapping.id}:${githubIssueId}`;
|
|
4264
4264
|
}
|
|
4265
|
+
function buildTrackedPullRequestIssueProgressKey(mapping, record) {
|
|
4266
|
+
return `${mapping.id}:pull-request:${record.paperclipIssueId}:${buildGitHubPullRequestReferenceKey({
|
|
4267
|
+
number: record.data.githubPullRequestNumber,
|
|
4268
|
+
repositoryUrl: record.data.repositoryUrl
|
|
4269
|
+
})}`;
|
|
4270
|
+
}
|
|
4265
4271
|
function buildImportedIssueRecord(mapping, issue, paperclipIssueId, importedAt) {
|
|
4266
4272
|
return {
|
|
4267
4273
|
mappingId: mapping.id,
|
|
@@ -5104,6 +5110,27 @@ function buildSyncFallbackExecutionStatePatch(params) {
|
|
|
5104
5110
|
}
|
|
5105
5111
|
return void 0;
|
|
5106
5112
|
}
|
|
5113
|
+
function describeGitHubLinkedPullRequestsStatusReason(linkedPullRequests) {
|
|
5114
|
+
const linkedPullRequestSubject = linkedPullRequests.length === 1 ? "the linked pull request" : "linked pull requests";
|
|
5115
|
+
const linkedPullRequestVerb = linkedPullRequests.length === 1 ? "has" : "have";
|
|
5116
|
+
const blockingConditions = [...new Set(
|
|
5117
|
+
linkedPullRequests.flatMap((pullRequest) => listGitHubPullRequestSyncBlockingConditions(pullRequest))
|
|
5118
|
+
)];
|
|
5119
|
+
const hasUnfinishedCi = linkedPullRequests.some((pullRequest) => pullRequest.ciState === "unfinished");
|
|
5120
|
+
const hasUnknownMergeability = linkedPullRequests.some(
|
|
5121
|
+
(pullRequest) => pullRequest.mergeStateStatus === "unknown"
|
|
5122
|
+
);
|
|
5123
|
+
if (blockingConditions.length > 0) {
|
|
5124
|
+
return `${linkedPullRequestSubject} ${linkedPullRequestVerb} ${formatPlainTextList(blockingConditions)}`;
|
|
5125
|
+
}
|
|
5126
|
+
if (hasUnfinishedCi) {
|
|
5127
|
+
return `${linkedPullRequestSubject} still ${linkedPullRequestVerb} unfinished CI jobs`;
|
|
5128
|
+
}
|
|
5129
|
+
if (hasUnknownMergeability) {
|
|
5130
|
+
return `${linkedPullRequestSubject} ${linkedPullRequestVerb} unknown mergeability`;
|
|
5131
|
+
}
|
|
5132
|
+
return `${linkedPullRequestSubject} ${linkedPullRequestVerb} green CI with all review threads resolved`;
|
|
5133
|
+
}
|
|
5107
5134
|
function describeGitHubStatusTransitionReason(params) {
|
|
5108
5135
|
const { snapshot, hasTrustedNewComment, maintainerAuthoredImportedIssue } = params;
|
|
5109
5136
|
if (snapshot.state === "closed") {
|
|
@@ -5125,25 +5152,7 @@ function describeGitHubStatusTransitionReason(params) {
|
|
|
5125
5152
|
}
|
|
5126
5153
|
return "the GitHub issue is open with no linked pull requests";
|
|
5127
5154
|
}
|
|
5128
|
-
|
|
5129
|
-
const linkedPullRequestVerb = snapshot.linkedPullRequests.length === 1 ? "has" : "have";
|
|
5130
|
-
const blockingConditions = [...new Set(
|
|
5131
|
-
snapshot.linkedPullRequests.flatMap((pullRequest) => listGitHubPullRequestSyncBlockingConditions(pullRequest))
|
|
5132
|
-
)];
|
|
5133
|
-
const hasUnfinishedCi = snapshot.linkedPullRequests.some((pullRequest) => pullRequest.ciState === "unfinished");
|
|
5134
|
-
const hasUnknownMergeability = snapshot.linkedPullRequests.some(
|
|
5135
|
-
(pullRequest) => pullRequest.mergeStateStatus === "unknown"
|
|
5136
|
-
);
|
|
5137
|
-
if (blockingConditions.length > 0) {
|
|
5138
|
-
return `${linkedPullRequestSubject} ${linkedPullRequestVerb} ${formatPlainTextList(blockingConditions)}`;
|
|
5139
|
-
}
|
|
5140
|
-
if (hasUnfinishedCi) {
|
|
5141
|
-
return `${linkedPullRequestSubject} still ${linkedPullRequestVerb} unfinished CI jobs`;
|
|
5142
|
-
}
|
|
5143
|
-
if (hasUnknownMergeability) {
|
|
5144
|
-
return `${linkedPullRequestSubject} ${linkedPullRequestVerb} unknown mergeability`;
|
|
5145
|
-
}
|
|
5146
|
-
return `${linkedPullRequestSubject} ${linkedPullRequestVerb} green CI with all review threads resolved`;
|
|
5155
|
+
return describeGitHubLinkedPullRequestsStatusReason(snapshot.linkedPullRequests);
|
|
5147
5156
|
}
|
|
5148
5157
|
function buildStatusTransitionCommentAnnotation(params) {
|
|
5149
5158
|
const { repository, snapshot, previousStatus, nextStatus, reason } = params;
|
|
@@ -5186,6 +5195,10 @@ function buildPaperclipIssueStatusTransitionComment(params) {
|
|
|
5186
5195
|
})
|
|
5187
5196
|
};
|
|
5188
5197
|
}
|
|
5198
|
+
function buildPaperclipPullRequestIssueStatusTransitionComment(params) {
|
|
5199
|
+
const reason = describeGitHubLinkedPullRequestsStatusReason([params.pullRequest]);
|
|
5200
|
+
return `GitHub Sync updated the status from \`${formatPaperclipIssueStatus(params.previousStatus)}\` to \`${formatPaperclipIssueStatus(params.nextStatus)}\` because ${reason}.`;
|
|
5201
|
+
}
|
|
5189
5202
|
function resolvePaperclipIssueStatus(params) {
|
|
5190
5203
|
const {
|
|
5191
5204
|
currentStatus,
|
|
@@ -5218,6 +5231,15 @@ function resolvePaperclipIssueStatus(params) {
|
|
|
5218
5231
|
}
|
|
5219
5232
|
return currentStatus;
|
|
5220
5233
|
}
|
|
5234
|
+
function resolvePaperclipPullRequestIssueStatus(params) {
|
|
5235
|
+
const { currentStatus, pullRequest, hasExecutorHandoffTarget } = params;
|
|
5236
|
+
if (currentStatus === "done" || currentStatus === "cancelled") {
|
|
5237
|
+
return currentStatus;
|
|
5238
|
+
}
|
|
5239
|
+
return resolvePaperclipStatusFromLinkedPullRequests([pullRequest], {
|
|
5240
|
+
preferInProgress: hasExecutorHandoffTarget
|
|
5241
|
+
});
|
|
5242
|
+
}
|
|
5221
5243
|
async function listLinkedPullRequestsForIssue(octokit, repository, issueNumber) {
|
|
5222
5244
|
const linkedPullRequests = [];
|
|
5223
5245
|
const seenPullRequestKeys = /* @__PURE__ */ new Set();
|
|
@@ -6531,6 +6553,52 @@ async function listGitHubPullRequestLinkRecords(ctx, query = {}) {
|
|
|
6531
6553
|
}
|
|
6532
6554
|
return records;
|
|
6533
6555
|
}
|
|
6556
|
+
function doesGitHubPullRequestLinkRecordMatchMapping(record, mapping) {
|
|
6557
|
+
if (record.data.repositoryUrl !== getNormalizedMappingRepositoryUrl(mapping)) {
|
|
6558
|
+
return false;
|
|
6559
|
+
}
|
|
6560
|
+
if (record.data.companyId && record.data.companyId !== mapping.companyId) {
|
|
6561
|
+
return false;
|
|
6562
|
+
}
|
|
6563
|
+
if (record.data.paperclipProjectId && record.data.paperclipProjectId !== mapping.paperclipProjectId) {
|
|
6564
|
+
return false;
|
|
6565
|
+
}
|
|
6566
|
+
return Boolean(mapping.companyId && mapping.paperclipProjectId);
|
|
6567
|
+
}
|
|
6568
|
+
function doesGitHubPullRequestLinkRecordMatchTarget(record, target) {
|
|
6569
|
+
if (!target) {
|
|
6570
|
+
return true;
|
|
6571
|
+
}
|
|
6572
|
+
switch (target.kind) {
|
|
6573
|
+
case "company":
|
|
6574
|
+
return !record.data.companyId || record.data.companyId === target.companyId;
|
|
6575
|
+
case "project":
|
|
6576
|
+
return (!record.data.companyId || record.data.companyId === target.companyId) && (!record.data.paperclipProjectId || record.data.paperclipProjectId === target.projectId);
|
|
6577
|
+
case "issue":
|
|
6578
|
+
return Boolean(target.issueId && record.paperclipIssueId === target.issueId);
|
|
6579
|
+
default:
|
|
6580
|
+
return true;
|
|
6581
|
+
}
|
|
6582
|
+
}
|
|
6583
|
+
async function listGitHubPullRequestIssueLinksForMapping(ctx, mapping, target) {
|
|
6584
|
+
const records = await listGitHubPullRequestLinkRecords(ctx, {
|
|
6585
|
+
...target?.kind === "issue" && target.issueId ? { paperclipIssueId: target.issueId } : {}
|
|
6586
|
+
});
|
|
6587
|
+
const recordsByKey = /* @__PURE__ */ new Map();
|
|
6588
|
+
for (const record of records) {
|
|
6589
|
+
if (!doesGitHubPullRequestLinkRecordMatchMapping(record, mapping) || !doesGitHubPullRequestLinkRecordMatchTarget(record, target)) {
|
|
6590
|
+
continue;
|
|
6591
|
+
}
|
|
6592
|
+
recordsByKey.set(
|
|
6593
|
+
`${record.paperclipIssueId}:${buildGitHubPullRequestReferenceKey({
|
|
6594
|
+
number: record.data.githubPullRequestNumber,
|
|
6595
|
+
repositoryUrl: record.data.repositoryUrl
|
|
6596
|
+
})}`,
|
|
6597
|
+
record
|
|
6598
|
+
);
|
|
6599
|
+
}
|
|
6600
|
+
return [...recordsByKey.values()];
|
|
6601
|
+
}
|
|
6534
6602
|
async function findStoredStatusTransitionCommentAnnotation(ctx, params) {
|
|
6535
6603
|
const issueId = params.issueId.trim();
|
|
6536
6604
|
const commentId = params.commentId.trim();
|
|
@@ -8140,6 +8208,171 @@ async function synchronizePaperclipIssueStatuses(ctx, octokit, repository, mappi
|
|
|
8140
8208
|
updatedDescriptionsCount
|
|
8141
8209
|
};
|
|
8142
8210
|
}
|
|
8211
|
+
async function synchronizePaperclipPullRequestIssueStatuses(ctx, octokit, mapping, advancedSettings, pullRequestLinks, paperclipApiBaseUrl, pullRequestStatusCache, syncFailureContext, failures, assertNotCancelled, onProgress) {
|
|
8212
|
+
if (!mapping.companyId || !mapping.paperclipProjectId || !ctx.issues || typeof ctx.issues.get !== "function" || typeof ctx.issues.update !== "function") {
|
|
8213
|
+
return {
|
|
8214
|
+
updatedStatusesCount: 0
|
|
8215
|
+
};
|
|
8216
|
+
}
|
|
8217
|
+
let updatedStatusesCount = 0;
|
|
8218
|
+
let completedIssueCount = 0;
|
|
8219
|
+
const mappingCompanyId = mapping.companyId;
|
|
8220
|
+
const mappingProjectId = mapping.paperclipProjectId;
|
|
8221
|
+
const totalIssueCount = pullRequestLinks.length;
|
|
8222
|
+
const queuedIssueWakeups = [];
|
|
8223
|
+
for (const pullRequestLink of pullRequestLinks) {
|
|
8224
|
+
if (assertNotCancelled) {
|
|
8225
|
+
await assertNotCancelled();
|
|
8226
|
+
}
|
|
8227
|
+
try {
|
|
8228
|
+
const pullRequestRepository = requireRepositoryReference(pullRequestLink.data.repositoryUrl);
|
|
8229
|
+
updateSyncFailureContext(syncFailureContext, {
|
|
8230
|
+
phase: "evaluating_github_status",
|
|
8231
|
+
repositoryUrl: pullRequestRepository.url,
|
|
8232
|
+
githubIssueNumber: void 0
|
|
8233
|
+
});
|
|
8234
|
+
const pullRequestResponse = await octokit.rest.pulls.get({
|
|
8235
|
+
owner: pullRequestRepository.owner,
|
|
8236
|
+
repo: pullRequestRepository.repo,
|
|
8237
|
+
pull_number: pullRequestLink.data.githubPullRequestNumber,
|
|
8238
|
+
headers: {
|
|
8239
|
+
"X-GitHub-Api-Version": GITHUB_API_VERSION
|
|
8240
|
+
}
|
|
8241
|
+
});
|
|
8242
|
+
const livePullRequestState = getPullRequestApiState({
|
|
8243
|
+
state: pullRequestResponse.data.state,
|
|
8244
|
+
merged: pullRequestResponse.data.merged
|
|
8245
|
+
}) === "open" ? "open" : "closed";
|
|
8246
|
+
if (livePullRequestState !== pullRequestLink.data.githubPullRequestState || pullRequestResponse.data.html_url !== pullRequestLink.data.githubPullRequestUrl || pullRequestResponse.data.title !== pullRequestLink.data.title) {
|
|
8247
|
+
await upsertGitHubPullRequestLinkRecord(ctx, {
|
|
8248
|
+
companyId: pullRequestLink.data.companyId ?? mappingCompanyId,
|
|
8249
|
+
projectId: pullRequestLink.data.paperclipProjectId ?? mappingProjectId,
|
|
8250
|
+
issueId: pullRequestLink.paperclipIssueId,
|
|
8251
|
+
repositoryUrl: pullRequestRepository.url,
|
|
8252
|
+
pullRequestNumber: pullRequestLink.data.githubPullRequestNumber,
|
|
8253
|
+
pullRequestUrl: pullRequestResponse.data.html_url ?? pullRequestLink.data.githubPullRequestUrl,
|
|
8254
|
+
pullRequestTitle: pullRequestResponse.data.title || pullRequestLink.data.title || `Pull request #${pullRequestLink.data.githubPullRequestNumber}`,
|
|
8255
|
+
pullRequestState: livePullRequestState
|
|
8256
|
+
});
|
|
8257
|
+
}
|
|
8258
|
+
if (livePullRequestState !== "open") {
|
|
8259
|
+
continue;
|
|
8260
|
+
}
|
|
8261
|
+
const pullRequest = await getGitHubPullRequestStatusSnapshot(
|
|
8262
|
+
octokit,
|
|
8263
|
+
pullRequestRepository,
|
|
8264
|
+
pullRequestLink.data.githubPullRequestNumber,
|
|
8265
|
+
pullRequestStatusCache
|
|
8266
|
+
);
|
|
8267
|
+
const paperclipIssue = await ctx.issues.get(pullRequestLink.paperclipIssueId, mapping.companyId);
|
|
8268
|
+
if (!paperclipIssue) {
|
|
8269
|
+
continue;
|
|
8270
|
+
}
|
|
8271
|
+
const paperclipIssueSyncContext = getPaperclipIssueSyncContext(paperclipIssue);
|
|
8272
|
+
const executorTransitionAssignee = resolvePaperclipIssueExecutorAssignee(
|
|
8273
|
+
paperclipIssueSyncContext,
|
|
8274
|
+
advancedSettings
|
|
8275
|
+
);
|
|
8276
|
+
const nextStatus = resolvePaperclipPullRequestIssueStatus({
|
|
8277
|
+
currentStatus: paperclipIssue.status,
|
|
8278
|
+
pullRequest,
|
|
8279
|
+
hasExecutorHandoffTarget: Boolean(executorTransitionAssignee)
|
|
8280
|
+
});
|
|
8281
|
+
const nextTransitionAssignee = resolveSyncTransitionAssignee({
|
|
8282
|
+
nextStatus,
|
|
8283
|
+
syncContext: paperclipIssueSyncContext,
|
|
8284
|
+
advancedSettings
|
|
8285
|
+
});
|
|
8286
|
+
const shouldClearTransitionAssignee = nextStatus === "in_review" && nextTransitionAssignee === null && paperclipIssueSyncContext.assignee !== null;
|
|
8287
|
+
const nextAssigneeChanged = nextTransitionAssignee ? !doesPaperclipIssueAssigneeMatch(paperclipIssueSyncContext.assignee, nextTransitionAssignee.principal) : false;
|
|
8288
|
+
const shouldWakeTransitionAssignee = paperclipIssue.status !== nextStatus && nextTransitionAssignee?.principal.kind === "agent" && isActionablePaperclipIssueStatus(nextStatus) && (nextAssigneeChanged || paperclipIssue.status !== nextStatus);
|
|
8289
|
+
if (paperclipIssue.status === nextStatus) {
|
|
8290
|
+
if (shouldClearTransitionAssignee) {
|
|
8291
|
+
updateSyncFailureContext(syncFailureContext, {
|
|
8292
|
+
phase: "updating_paperclip_status",
|
|
8293
|
+
repositoryUrl: pullRequestRepository.url,
|
|
8294
|
+
githubIssueNumber: void 0
|
|
8295
|
+
});
|
|
8296
|
+
await updatePaperclipIssueState(ctx, {
|
|
8297
|
+
companyId: mapping.companyId,
|
|
8298
|
+
issueId: pullRequestLink.paperclipIssueId,
|
|
8299
|
+
currentStatus: paperclipIssue.status,
|
|
8300
|
+
syncContext: paperclipIssueSyncContext,
|
|
8301
|
+
nextStatus,
|
|
8302
|
+
clearAssignee: true,
|
|
8303
|
+
transitionComment: "",
|
|
8304
|
+
paperclipApiBaseUrl
|
|
8305
|
+
});
|
|
8306
|
+
}
|
|
8307
|
+
continue;
|
|
8308
|
+
}
|
|
8309
|
+
const transitionComment = buildPaperclipPullRequestIssueStatusTransitionComment({
|
|
8310
|
+
previousStatus: paperclipIssue.status,
|
|
8311
|
+
nextStatus,
|
|
8312
|
+
pullRequest
|
|
8313
|
+
});
|
|
8314
|
+
updateSyncFailureContext(syncFailureContext, {
|
|
8315
|
+
phase: "updating_paperclip_status",
|
|
8316
|
+
repositoryUrl: pullRequestRepository.url,
|
|
8317
|
+
githubIssueNumber: void 0
|
|
8318
|
+
});
|
|
8319
|
+
await updatePaperclipIssueState(ctx, {
|
|
8320
|
+
companyId: mapping.companyId,
|
|
8321
|
+
issueId: pullRequestLink.paperclipIssueId,
|
|
8322
|
+
currentStatus: paperclipIssue.status,
|
|
8323
|
+
syncContext: paperclipIssueSyncContext,
|
|
8324
|
+
nextStatus,
|
|
8325
|
+
...nextTransitionAssignee ? { nextAssignee: nextTransitionAssignee.principal } : {},
|
|
8326
|
+
...shouldClearTransitionAssignee ? { clearAssignee: true } : {},
|
|
8327
|
+
transitionComment,
|
|
8328
|
+
paperclipApiBaseUrl
|
|
8329
|
+
});
|
|
8330
|
+
updatedStatusesCount += 1;
|
|
8331
|
+
if (shouldWakeTransitionAssignee && nextTransitionAssignee?.principal.kind === "agent") {
|
|
8332
|
+
queuedIssueWakeups.push({
|
|
8333
|
+
assigneeAgentId: nextTransitionAssignee.principal.id,
|
|
8334
|
+
paperclipIssueId: pullRequestLink.paperclipIssueId,
|
|
8335
|
+
reason: STATUS_TRANSITION_WAKE_REASON,
|
|
8336
|
+
mutation: "status_transition",
|
|
8337
|
+
previousStatus: paperclipIssue.status,
|
|
8338
|
+
nextStatus
|
|
8339
|
+
});
|
|
8340
|
+
}
|
|
8341
|
+
} catch (error) {
|
|
8342
|
+
if (isGitHubRateLimitError(error)) {
|
|
8343
|
+
throw error;
|
|
8344
|
+
}
|
|
8345
|
+
recordRecoverableSyncFailure(ctx, failures, error, syncFailureContext);
|
|
8346
|
+
continue;
|
|
8347
|
+
} finally {
|
|
8348
|
+
completedIssueCount += 1;
|
|
8349
|
+
if (onProgress) {
|
|
8350
|
+
await onProgress({
|
|
8351
|
+
pullRequestLink,
|
|
8352
|
+
completedIssueCount,
|
|
8353
|
+
totalIssueCount
|
|
8354
|
+
});
|
|
8355
|
+
}
|
|
8356
|
+
}
|
|
8357
|
+
}
|
|
8358
|
+
await mapWithConcurrency(
|
|
8359
|
+
queuedIssueWakeups,
|
|
8360
|
+
IMPORTED_ISSUE_WAKEUP_CONCURRENCY,
|
|
8361
|
+
async (queuedWakeup) => wakePaperclipIssueAssignee(ctx, {
|
|
8362
|
+
assigneeAgentId: queuedWakeup.assigneeAgentId,
|
|
8363
|
+
paperclipIssueId: queuedWakeup.paperclipIssueId,
|
|
8364
|
+
companyId: mapping.companyId,
|
|
8365
|
+
paperclipApiBaseUrl,
|
|
8366
|
+
reason: queuedWakeup.reason,
|
|
8367
|
+
mutation: queuedWakeup.mutation,
|
|
8368
|
+
previousStatus: queuedWakeup.previousStatus,
|
|
8369
|
+
nextStatus: queuedWakeup.nextStatus
|
|
8370
|
+
})
|
|
8371
|
+
);
|
|
8372
|
+
return {
|
|
8373
|
+
updatedStatusesCount
|
|
8374
|
+
};
|
|
8375
|
+
}
|
|
8143
8376
|
async function getResolvedConfig(ctx) {
|
|
8144
8377
|
const [savedConfig, externalConfig] = await Promise.all([
|
|
8145
8378
|
ctx.config.get(),
|
|
@@ -11697,6 +11930,14 @@ async function performSync(ctx, trigger, options = {}) {
|
|
|
11697
11930
|
completedTrackedIssueKeys.add(key);
|
|
11698
11931
|
completedTrackedIssueCount += 1;
|
|
11699
11932
|
}
|
|
11933
|
+
function markTrackedPullRequestIssueProcessed(mapping, record) {
|
|
11934
|
+
const key = buildTrackedPullRequestIssueProgressKey(mapping, record);
|
|
11935
|
+
if (completedTrackedIssueKeys.has(key)) {
|
|
11936
|
+
return;
|
|
11937
|
+
}
|
|
11938
|
+
completedTrackedIssueKeys.add(key);
|
|
11939
|
+
completedTrackedIssueCount += 1;
|
|
11940
|
+
}
|
|
11700
11941
|
function recordCompanyBacklogSnapshotsFromPlans(repositoryPlans2) {
|
|
11701
11942
|
if (options.target?.kind === "project" || options.target?.kind === "issue") {
|
|
11702
11943
|
return;
|
|
@@ -11804,12 +12045,13 @@ async function performSync(ctx, trigger, options = {}) {
|
|
|
11804
12045
|
const importRegistryByIssueId = new Map(
|
|
11805
12046
|
importedIssueRecords.map((entry) => [entry.githubIssueId, entry])
|
|
11806
12047
|
);
|
|
12048
|
+
const pullRequestLinks = await listGitHubPullRequestIssueLinksForMapping(ctx, mapping, options.target);
|
|
11807
12049
|
const ensuredPaperclipIssueIds = /* @__PURE__ */ new Map();
|
|
11808
12050
|
const trackedIssueIds = /* @__PURE__ */ new Set([
|
|
11809
12051
|
...issues.map((issue) => issue.id),
|
|
11810
12052
|
...importRegistryByIssueId.keys()
|
|
11811
12053
|
]);
|
|
11812
|
-
const trackedIssueCount = [...trackedIssueIds].filter((issueId) => allIssuesById.has(issueId)).length;
|
|
12054
|
+
const trackedIssueCount = [...trackedIssueIds].filter((issueId) => allIssuesById.has(issueId)).length + pullRequestLinks.length;
|
|
11813
12055
|
totalTrackedIssueCount += trackedIssueCount;
|
|
11814
12056
|
syncedIssuesCount = totalTrackedIssueCount;
|
|
11815
12057
|
currentProgress = {
|
|
@@ -11828,6 +12070,7 @@ async function performSync(ctx, trigger, options = {}) {
|
|
|
11828
12070
|
allIssues: eligibleIssues,
|
|
11829
12071
|
issues,
|
|
11830
12072
|
allIssuesById,
|
|
12073
|
+
pullRequestLinks,
|
|
11831
12074
|
trackedIssueCount
|
|
11832
12075
|
});
|
|
11833
12076
|
} catch (error) {
|
|
@@ -11863,7 +12106,7 @@ async function performSync(ctx, trigger, options = {}) {
|
|
|
11863
12106
|
for (const plan of repositoryPlans) {
|
|
11864
12107
|
await throwIfSyncCancelled();
|
|
11865
12108
|
try {
|
|
11866
|
-
const { mapping, advancedSettings, repository, repositoryIndex, allIssuesById, issues } = plan;
|
|
12109
|
+
const { mapping, advancedSettings, repository, repositoryIndex, allIssuesById, issues, pullRequestLinks } = plan;
|
|
11867
12110
|
const companyId = mapping.companyId;
|
|
11868
12111
|
let availableLabels = companyId ? companyLabelDirectoryCache.get(companyId) : void 0;
|
|
11869
12112
|
if (!availableLabels) {
|
|
@@ -11916,6 +12159,15 @@ async function performSync(ctx, trigger, options = {}) {
|
|
|
11916
12159
|
}
|
|
11917
12160
|
}
|
|
11918
12161
|
}
|
|
12162
|
+
for (const pullRequestLink of pullRequestLinks) {
|
|
12163
|
+
const pullRequestRepository = requireRepositoryReference(pullRequestLink.data.repositoryUrl);
|
|
12164
|
+
const entry = openLinkedPullRequestNumbersByRepository.get(pullRequestRepository.url) ?? {
|
|
12165
|
+
repository: pullRequestRepository,
|
|
12166
|
+
numbers: /* @__PURE__ */ new Set()
|
|
12167
|
+
};
|
|
12168
|
+
entry.numbers.add(pullRequestLink.data.githubPullRequestNumber);
|
|
12169
|
+
openLinkedPullRequestNumbersByRepository.set(pullRequestRepository.url, entry);
|
|
12170
|
+
}
|
|
11919
12171
|
for (const entry of openLinkedPullRequestNumbersByRepository.values()) {
|
|
11920
12172
|
await warmGitHubPullRequestStatusCache(
|
|
11921
12173
|
octokit,
|
|
@@ -12053,6 +12305,33 @@ async function performSync(ctx, trigger, options = {}) {
|
|
|
12053
12305
|
updatedStatusesCount += synchronizationResult.updatedStatusesCount;
|
|
12054
12306
|
updatedLabelsCount += synchronizationResult.updatedLabelsCount;
|
|
12055
12307
|
updatedDescriptionsCount += synchronizationResult.updatedDescriptionsCount;
|
|
12308
|
+
const pullRequestSynchronizationResult = await synchronizePaperclipPullRequestIssueStatuses(
|
|
12309
|
+
ctx,
|
|
12310
|
+
octokit,
|
|
12311
|
+
mapping,
|
|
12312
|
+
advancedSettings,
|
|
12313
|
+
pullRequestLinks,
|
|
12314
|
+
paperclipApiBaseUrl,
|
|
12315
|
+
pullRequestStatusCache,
|
|
12316
|
+
failureContext,
|
|
12317
|
+
recoverableFailures,
|
|
12318
|
+
throwIfSyncCancelled,
|
|
12319
|
+
async (progress) => {
|
|
12320
|
+
markTrackedPullRequestIssueProcessed(mapping, progress.pullRequestLink);
|
|
12321
|
+
const pullRequestRepository = requireRepositoryReference(progress.pullRequestLink.data.repositoryUrl);
|
|
12322
|
+
currentProgress = {
|
|
12323
|
+
phase: "syncing",
|
|
12324
|
+
totalRepositoryCount: mappings.length,
|
|
12325
|
+
currentRepositoryIndex: repositoryIndex,
|
|
12326
|
+
currentRepositoryUrl: repository.url,
|
|
12327
|
+
completedIssueCount: completedTrackedIssueCount,
|
|
12328
|
+
totalIssueCount: totalTrackedIssueCount,
|
|
12329
|
+
detailLabel: `Synced pull request #${progress.pullRequestLink.data.githubPullRequestNumber} in ${pullRequestRepository.owner}/${pullRequestRepository.repo}.`
|
|
12330
|
+
};
|
|
12331
|
+
await persistRunningProgress(progress.completedIssueCount === progress.totalIssueCount);
|
|
12332
|
+
}
|
|
12333
|
+
);
|
|
12334
|
+
updatedStatusesCount += pullRequestSynchronizationResult.updatedStatusesCount;
|
|
12056
12335
|
} catch (error) {
|
|
12057
12336
|
if (error instanceof SyncCancellationError || isGitHubRateLimitError(error)) {
|
|
12058
12337
|
throw error;
|
package/package.json
CHANGED
|
@@ -1,10 +1,10 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "paperclip-github-plugin",
|
|
3
|
-
"version": "0.6.
|
|
3
|
+
"version": "0.6.1",
|
|
4
4
|
"description": "Paperclip plugin for synchronizing GitHub issues into Paperclip projects.",
|
|
5
5
|
"license": "Apache-2.0",
|
|
6
6
|
"type": "module",
|
|
7
|
-
"packageManager": "pnpm@10.33.
|
|
7
|
+
"packageManager": "pnpm@10.33.2",
|
|
8
8
|
"engines": {
|
|
9
9
|
"node": ">=20"
|
|
10
10
|
},
|