paperclip-github-plugin 0.8.2 → 0.8.3
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 +6 -1
- package/dist/manifest.js +1 -1
- package/dist/ui/index.js +50 -1
- package/dist/ui/index.js.map +2 -2
- package/dist/worker.js +445 -2
- package/package.json +1 -1
package/dist/worker.js
CHANGED
|
@@ -613,6 +613,7 @@ function requiresPaperclipBoardAccess(value) {
|
|
|
613
613
|
}
|
|
614
614
|
|
|
615
615
|
// src/worker.ts
|
|
616
|
+
var GITHUB_SYNC_BASE_ORIGIN_KIND = `plugin:${GITHUB_SYNC_PLUGIN_ID}`;
|
|
616
617
|
var GITHUB_ISSUE_ORIGIN_KIND = `plugin:${GITHUB_SYNC_PLUGIN_ID}:github-issue`;
|
|
617
618
|
var GITHUB_PULL_REQUEST_ORIGIN_KIND = `plugin:${GITHUB_SYNC_PLUGIN_ID}:github-pull-request`;
|
|
618
619
|
var SETTINGS_SCOPE = {
|
|
@@ -5201,6 +5202,9 @@ function isHealthyMaintainerWaitTransition(params) {
|
|
|
5201
5202
|
const { currentStatus, nextStatus, syncContext } = params;
|
|
5202
5203
|
return nextStatus === "in_review" && (currentStatus === "done" || currentStatus === "in_review") && syncContext.executionState === null && syncContext.executionPolicy !== null;
|
|
5203
5204
|
}
|
|
5205
|
+
function shouldPreserveImportedTriageAssignee(params) {
|
|
5206
|
+
return params.wasImportedThisRun && params.maintainerAuthoredImportedIssue === true && params.currentStatus === "backlog" && params.nextStatus === "todo";
|
|
5207
|
+
}
|
|
5204
5208
|
function doesPaperclipIssueAssigneeMatch(currentAssignee, nextAssignee) {
|
|
5205
5209
|
return isSamePaperclipIssueAssigneePrincipal(currentAssignee, nextAssignee);
|
|
5206
5210
|
}
|
|
@@ -6365,6 +6369,18 @@ function extractImportedGitHubIssueUrlFromDescription(description) {
|
|
|
6365
6369
|
}
|
|
6366
6370
|
return normalizeGitHubIssueHtmlUrl(legacyMatch[1]);
|
|
6367
6371
|
}
|
|
6372
|
+
function removeGitHubIssueLinkMetadataFromDescription(description) {
|
|
6373
|
+
if (typeof description !== "string") {
|
|
6374
|
+
return void 0;
|
|
6375
|
+
}
|
|
6376
|
+
let next = description;
|
|
6377
|
+
const hiddenMarkerPattern = getHiddenGitHubImportMarkerPattern();
|
|
6378
|
+
while (hiddenMarkerPattern.test(next)) {
|
|
6379
|
+
next = next.replace(hiddenMarkerPattern, "");
|
|
6380
|
+
}
|
|
6381
|
+
next = next.replace(/^\*\s+GitHub issue:\s+\[[^\]]+\]\([^)]+\)\s*$/gm, "").replace(/^Imported from https:\/\/github\.com\/[^\s)]+\/[^\s)]+\/issues\/\d+\.?\s*$/gim, "").replace(/[ \t]+\n/g, "\n").replace(/\n{3,}/g, "\n\n").trim();
|
|
6382
|
+
return next === description ? void 0 : next;
|
|
6383
|
+
}
|
|
6368
6384
|
function compareImportedPaperclipIssueCreatedAt(left, right) {
|
|
6369
6385
|
const leftTime = Date.parse(String(left.createdAt ?? ""));
|
|
6370
6386
|
const rightTime = Date.parse(String(right.createdAt ?? ""));
|
|
@@ -6693,6 +6709,7 @@ async function listGitHubIssueLinkRecords(ctx, query = {}) {
|
|
|
6693
6709
|
}
|
|
6694
6710
|
records.push({
|
|
6695
6711
|
paperclipIssueId: entry.scopeId,
|
|
6712
|
+
...typeof entry.externalId === "string" && entry.externalId.trim() ? { externalId: entry.externalId.trim() } : {},
|
|
6696
6713
|
...typeof entry.createdAt === "string" ? { createdAt: entry.createdAt } : {},
|
|
6697
6714
|
...typeof entry.updatedAt === "string" ? { updatedAt: entry.updatedAt } : {},
|
|
6698
6715
|
...typeof entry.title === "string" && entry.title.trim() ? { title: entry.title.trim() } : {},
|
|
@@ -6739,6 +6756,7 @@ async function listGitHubPullRequestLinkRecords(ctx, query = {}) {
|
|
|
6739
6756
|
}
|
|
6740
6757
|
records.push({
|
|
6741
6758
|
paperclipIssueId: entry.scopeId,
|
|
6759
|
+
...typeof entry.externalId === "string" && entry.externalId.trim() ? { externalId: entry.externalId.trim() } : {},
|
|
6742
6760
|
...typeof entry.createdAt === "string" ? { createdAt: entry.createdAt } : {},
|
|
6743
6761
|
...typeof entry.updatedAt === "string" ? { updatedAt: entry.updatedAt } : {},
|
|
6744
6762
|
...typeof entry.title === "string" && entry.title.trim() ? { title: entry.title.trim() } : {},
|
|
@@ -7147,6 +7165,134 @@ async function linkPaperclipIssueToGitHubPullRequest(ctx, params) {
|
|
|
7147
7165
|
githubPullRequestState: pullRequestState
|
|
7148
7166
|
};
|
|
7149
7167
|
}
|
|
7168
|
+
function invalidateGitHubLinkCachesForTarget(params) {
|
|
7169
|
+
if (!params.companyId || !params.projectId || !params.repositoryUrl) {
|
|
7170
|
+
return;
|
|
7171
|
+
}
|
|
7172
|
+
const repository = parseRepositoryReference(params.repositoryUrl);
|
|
7173
|
+
if (!repository) {
|
|
7174
|
+
return;
|
|
7175
|
+
}
|
|
7176
|
+
invalidateProjectPullRequestCaches({
|
|
7177
|
+
companyId: params.companyId,
|
|
7178
|
+
projectId: params.projectId,
|
|
7179
|
+
repository
|
|
7180
|
+
});
|
|
7181
|
+
}
|
|
7182
|
+
async function tombstoneGitHubIssueLinkRecord(ctx, record, unlinkedAt) {
|
|
7183
|
+
await ctx.entities.upsert({
|
|
7184
|
+
entityType: ISSUE_LINK_ENTITY_TYPE,
|
|
7185
|
+
scopeKind: "issue",
|
|
7186
|
+
scopeId: record.paperclipIssueId,
|
|
7187
|
+
externalId: record.externalId ?? record.data.githubIssueUrl,
|
|
7188
|
+
title: record.title ?? `GitHub issue #${record.data.githubIssueNumber}`,
|
|
7189
|
+
status: "unlinked",
|
|
7190
|
+
data: {
|
|
7191
|
+
kind: "issue",
|
|
7192
|
+
paperclipIssueId: record.paperclipIssueId,
|
|
7193
|
+
repositoryUrl: record.data.repositoryUrl,
|
|
7194
|
+
githubIssueNumber: record.data.githubIssueNumber,
|
|
7195
|
+
githubIssueUrl: record.data.githubIssueUrl,
|
|
7196
|
+
...record.data.companyId ? { companyId: record.data.companyId } : {},
|
|
7197
|
+
...record.data.paperclipProjectId ? { paperclipProjectId: record.data.paperclipProjectId } : {},
|
|
7198
|
+
unlinkedAt
|
|
7199
|
+
}
|
|
7200
|
+
});
|
|
7201
|
+
}
|
|
7202
|
+
async function tombstoneGitHubPullRequestLinkRecord(ctx, record, unlinkedAt) {
|
|
7203
|
+
await ctx.entities.upsert({
|
|
7204
|
+
entityType: PULL_REQUEST_LINK_ENTITY_TYPE,
|
|
7205
|
+
scopeKind: "issue",
|
|
7206
|
+
scopeId: record.paperclipIssueId,
|
|
7207
|
+
externalId: record.externalId ?? record.data.githubPullRequestUrl,
|
|
7208
|
+
title: record.title ?? `GitHub pull request #${record.data.githubPullRequestNumber}`,
|
|
7209
|
+
status: "unlinked",
|
|
7210
|
+
data: {
|
|
7211
|
+
kind: "pull_request",
|
|
7212
|
+
paperclipIssueId: record.paperclipIssueId,
|
|
7213
|
+
repositoryUrl: record.data.repositoryUrl,
|
|
7214
|
+
githubPullRequestNumber: record.data.githubPullRequestNumber,
|
|
7215
|
+
githubPullRequestUrl: record.data.githubPullRequestUrl,
|
|
7216
|
+
...record.data.companyId ? { companyId: record.data.companyId } : {},
|
|
7217
|
+
...record.data.paperclipProjectId ? { paperclipProjectId: record.data.paperclipProjectId } : {},
|
|
7218
|
+
unlinkedAt
|
|
7219
|
+
}
|
|
7220
|
+
});
|
|
7221
|
+
}
|
|
7222
|
+
async function unlinkPaperclipIssueFromGitHub(ctx, params) {
|
|
7223
|
+
const companyId = normalizeCompanyId(params.companyId);
|
|
7224
|
+
const issueId = normalizeOptionalString2(params.issueId);
|
|
7225
|
+
if (!companyId || !issueId) {
|
|
7226
|
+
throw new Error("companyId and issueId are required.");
|
|
7227
|
+
}
|
|
7228
|
+
const issue = await ctx.issues.get(issueId, companyId);
|
|
7229
|
+
if (!issue) {
|
|
7230
|
+
throw new Error("Paperclip issue was not found.");
|
|
7231
|
+
}
|
|
7232
|
+
const issueLinkRecords = (await listGitHubIssueLinkRecords(ctx, {
|
|
7233
|
+
paperclipIssueId: issueId
|
|
7234
|
+
})).filter((record) => !record.data.companyId || record.data.companyId === companyId);
|
|
7235
|
+
const pullRequestLinkRecords = (await listGitHubPullRequestLinkRecords(ctx, {
|
|
7236
|
+
paperclipIssueId: issueId
|
|
7237
|
+
})).filter((record) => !record.data.companyId || record.data.companyId === companyId);
|
|
7238
|
+
const importRegistry = normalizeImportRegistry(await ctx.state.get(IMPORT_REGISTRY_SCOPE));
|
|
7239
|
+
const removedImportRegistryEntries = importRegistry.filter(
|
|
7240
|
+
(entry) => entry.paperclipIssueId === issueId && (!entry.companyId || entry.companyId === companyId)
|
|
7241
|
+
);
|
|
7242
|
+
const nextImportRegistry = importRegistry.filter(
|
|
7243
|
+
(entry) => !(entry.paperclipIssueId === issueId && (!entry.companyId || entry.companyId === companyId))
|
|
7244
|
+
);
|
|
7245
|
+
const unlinkedAt = (/* @__PURE__ */ new Date()).toISOString();
|
|
7246
|
+
for (const record of issueLinkRecords) {
|
|
7247
|
+
await tombstoneGitHubIssueLinkRecord(ctx, record, unlinkedAt);
|
|
7248
|
+
invalidateGitHubLinkCachesForTarget({
|
|
7249
|
+
companyId: record.data.companyId ?? companyId,
|
|
7250
|
+
projectId: record.data.paperclipProjectId ?? issue.projectId ?? void 0,
|
|
7251
|
+
repositoryUrl: record.data.repositoryUrl
|
|
7252
|
+
});
|
|
7253
|
+
}
|
|
7254
|
+
for (const record of pullRequestLinkRecords) {
|
|
7255
|
+
await tombstoneGitHubPullRequestLinkRecord(ctx, record, unlinkedAt);
|
|
7256
|
+
invalidateGitHubLinkCachesForTarget({
|
|
7257
|
+
companyId: record.data.companyId ?? companyId,
|
|
7258
|
+
projectId: record.data.paperclipProjectId ?? issue.projectId ?? void 0,
|
|
7259
|
+
repositoryUrl: record.data.repositoryUrl
|
|
7260
|
+
});
|
|
7261
|
+
}
|
|
7262
|
+
for (const entry of removedImportRegistryEntries) {
|
|
7263
|
+
invalidateGitHubLinkCachesForTarget({
|
|
7264
|
+
companyId: entry.companyId ?? companyId,
|
|
7265
|
+
projectId: entry.paperclipProjectId ?? issue.projectId ?? void 0,
|
|
7266
|
+
repositoryUrl: entry.repositoryUrl
|
|
7267
|
+
});
|
|
7268
|
+
}
|
|
7269
|
+
if (removedImportRegistryEntries.length > 0) {
|
|
7270
|
+
await ctx.state.set(IMPORT_REGISTRY_SCOPE, nextImportRegistry);
|
|
7271
|
+
}
|
|
7272
|
+
const shouldClearGitHubOrigin = issue.originKind === GITHUB_ISSUE_ORIGIN_KIND || issue.originKind === GITHUB_PULL_REQUEST_ORIGIN_KIND;
|
|
7273
|
+
const nextDescription = removeGitHubIssueLinkMetadataFromDescription(issue.description);
|
|
7274
|
+
const issuePatch = {};
|
|
7275
|
+
if (shouldClearGitHubOrigin) {
|
|
7276
|
+
issuePatch.originKind = GITHUB_SYNC_BASE_ORIGIN_KIND;
|
|
7277
|
+
issuePatch.originId = null;
|
|
7278
|
+
issuePatch.originRunId = null;
|
|
7279
|
+
}
|
|
7280
|
+
if (nextDescription !== void 0) {
|
|
7281
|
+
issuePatch.description = nextDescription;
|
|
7282
|
+
}
|
|
7283
|
+
if (Object.keys(issuePatch).length > 0) {
|
|
7284
|
+
await ctx.issues.update(issueId, issuePatch, companyId);
|
|
7285
|
+
}
|
|
7286
|
+
return {
|
|
7287
|
+
paperclipIssueId: issueId,
|
|
7288
|
+
unlinked: issueLinkRecords.length > 0 || pullRequestLinkRecords.length > 0 || removedImportRegistryEntries.length > 0 || shouldClearGitHubOrigin || nextDescription !== void 0,
|
|
7289
|
+
unlinkedIssueLinksCount: issueLinkRecords.length,
|
|
7290
|
+
unlinkedPullRequestLinksCount: pullRequestLinkRecords.length,
|
|
7291
|
+
removedImportRegistryEntriesCount: removedImportRegistryEntries.length,
|
|
7292
|
+
clearedIssueOrigin: shouldClearGitHubOrigin,
|
|
7293
|
+
removedDescriptionMetadata: nextDescription !== void 0
|
|
7294
|
+
};
|
|
7295
|
+
}
|
|
7150
7296
|
async function upsertStatusTransitionCommentAnnotation(ctx, params) {
|
|
7151
7297
|
const { issueId, commentId, annotation } = params;
|
|
7152
7298
|
await ctx.entities.upsert({
|
|
@@ -8450,6 +8596,261 @@ async function ensurePaperclipIssueImported(ctx, mapping, advancedSettings, issu
|
|
|
8450
8596
|
}
|
|
8451
8597
|
return createdIssue.id;
|
|
8452
8598
|
}
|
|
8599
|
+
function getGitHubIssueRepositoryReference(issue) {
|
|
8600
|
+
return parseGitHubIssueHtmlUrl(issue.htmlUrl);
|
|
8601
|
+
}
|
|
8602
|
+
function findTransferredIssueTargetMapping(mappings, params) {
|
|
8603
|
+
const companyId = normalizeCompanyId(params.companyId);
|
|
8604
|
+
const repository = parseRepositoryReference(params.repositoryUrl);
|
|
8605
|
+
if (!companyId || !repository) {
|
|
8606
|
+
return void 0;
|
|
8607
|
+
}
|
|
8608
|
+
return getSyncableMappings(mappings).find((mapping) => {
|
|
8609
|
+
if (normalizeCompanyId(mapping.companyId) !== companyId) {
|
|
8610
|
+
return false;
|
|
8611
|
+
}
|
|
8612
|
+
const mappingRepository = parseRepositoryReference(mapping.repositoryUrl);
|
|
8613
|
+
return Boolean(mappingRepository && areRepositoriesEqual(mappingRepository, repository));
|
|
8614
|
+
});
|
|
8615
|
+
}
|
|
8616
|
+
async function patchPaperclipIssueForGitHubIssueTransfer(ctx, params) {
|
|
8617
|
+
const nextDescription = buildPaperclipIssueDescription(params.githubIssue, []);
|
|
8618
|
+
const nextOriginId = normalizeGitHubIssueHtmlUrl(params.githubIssue.htmlUrl) ?? params.githubIssue.htmlUrl;
|
|
8619
|
+
const descriptionUpdated = normalizeIssueDescriptionValue(params.currentDescription) !== nextDescription;
|
|
8620
|
+
const patch = {
|
|
8621
|
+
projectId: params.projectId,
|
|
8622
|
+
originKind: GITHUB_ISSUE_ORIGIN_KIND,
|
|
8623
|
+
originId: nextOriginId,
|
|
8624
|
+
...descriptionUpdated ? { description: nextDescription } : {}
|
|
8625
|
+
};
|
|
8626
|
+
let issueUpdated = false;
|
|
8627
|
+
if (params.paperclipApiBaseUrl) {
|
|
8628
|
+
try {
|
|
8629
|
+
const response = await fetchPaperclipApi(
|
|
8630
|
+
getPaperclipIssueEndpoint(params.paperclipApiBaseUrl, params.issueId),
|
|
8631
|
+
{
|
|
8632
|
+
method: "PATCH",
|
|
8633
|
+
headers: {
|
|
8634
|
+
accept: "application/json",
|
|
8635
|
+
"content-type": "application/json"
|
|
8636
|
+
},
|
|
8637
|
+
body: JSON.stringify(patch)
|
|
8638
|
+
},
|
|
8639
|
+
{
|
|
8640
|
+
companyId: params.companyId
|
|
8641
|
+
}
|
|
8642
|
+
);
|
|
8643
|
+
const payloadResult = await readPaperclipApiJsonResponse(response, {
|
|
8644
|
+
operationLabel: "issue update",
|
|
8645
|
+
bodyRequired: false
|
|
8646
|
+
});
|
|
8647
|
+
issueUpdated = !payloadResult.failure;
|
|
8648
|
+
} catch {
|
|
8649
|
+
issueUpdated = false;
|
|
8650
|
+
}
|
|
8651
|
+
}
|
|
8652
|
+
if (!issueUpdated) {
|
|
8653
|
+
await ctx.issues.update(
|
|
8654
|
+
params.issueId,
|
|
8655
|
+
patch,
|
|
8656
|
+
params.companyId
|
|
8657
|
+
);
|
|
8658
|
+
}
|
|
8659
|
+
return {
|
|
8660
|
+
descriptionUpdated
|
|
8661
|
+
};
|
|
8662
|
+
}
|
|
8663
|
+
async function moveImportedIssueToTransferredGitHubMapping(ctx, params) {
|
|
8664
|
+
const companyId = normalizeCompanyId(params.targetMapping.companyId);
|
|
8665
|
+
const projectId = normalizeOptionalString2(params.targetMapping.paperclipProjectId);
|
|
8666
|
+
if (!companyId || !projectId) {
|
|
8667
|
+
return {
|
|
8668
|
+
updatedDescriptionsCount: 0
|
|
8669
|
+
};
|
|
8670
|
+
}
|
|
8671
|
+
const paperclipIssue = await ctx.issues.get(params.importedIssue.paperclipIssueId, companyId);
|
|
8672
|
+
if (!paperclipIssue) {
|
|
8673
|
+
return {
|
|
8674
|
+
updatedDescriptionsCount: 0
|
|
8675
|
+
};
|
|
8676
|
+
}
|
|
8677
|
+
const transferPatchResult = await patchPaperclipIssueForGitHubIssueTransfer(ctx, {
|
|
8678
|
+
companyId,
|
|
8679
|
+
issueId: params.importedIssue.paperclipIssueId,
|
|
8680
|
+
projectId,
|
|
8681
|
+
githubIssue: params.githubIssue,
|
|
8682
|
+
currentDescription: paperclipIssue.description,
|
|
8683
|
+
paperclipApiBaseUrl: params.paperclipApiBaseUrl
|
|
8684
|
+
});
|
|
8685
|
+
const nextGitHubIssueUrl = normalizeGitHubIssueHtmlUrl(params.githubIssue.htmlUrl) ?? params.githubIssue.htmlUrl;
|
|
8686
|
+
const existingLinks = await listGitHubIssueLinkRecords(ctx, {
|
|
8687
|
+
paperclipIssueId: params.importedIssue.paperclipIssueId
|
|
8688
|
+
});
|
|
8689
|
+
const unlinkedAt = (/* @__PURE__ */ new Date()).toISOString();
|
|
8690
|
+
for (const link of existingLinks) {
|
|
8691
|
+
if ((!link.data.companyId || link.data.companyId === companyId) && link.data.githubIssueUrl !== nextGitHubIssueUrl) {
|
|
8692
|
+
await tombstoneGitHubIssueLinkRecord(ctx, link, unlinkedAt);
|
|
8693
|
+
}
|
|
8694
|
+
}
|
|
8695
|
+
await upsertGitHubIssueLinkRecord(
|
|
8696
|
+
ctx,
|
|
8697
|
+
params.targetMapping,
|
|
8698
|
+
params.importedIssue.paperclipIssueId,
|
|
8699
|
+
params.githubIssue,
|
|
8700
|
+
[]
|
|
8701
|
+
);
|
|
8702
|
+
upsertImportedIssueRecord(
|
|
8703
|
+
params.nextRegistry,
|
|
8704
|
+
buildImportedIssueRecord(
|
|
8705
|
+
params.targetMapping,
|
|
8706
|
+
params.githubIssue,
|
|
8707
|
+
params.importedIssue.paperclipIssueId,
|
|
8708
|
+
params.importedIssue.importedAt
|
|
8709
|
+
)
|
|
8710
|
+
);
|
|
8711
|
+
invalidateGitHubLinkCachesForTarget({
|
|
8712
|
+
companyId,
|
|
8713
|
+
projectId: params.sourceMapping.paperclipProjectId ?? paperclipIssue.projectId ?? void 0,
|
|
8714
|
+
repositoryUrl: params.sourceMapping.repositoryUrl
|
|
8715
|
+
});
|
|
8716
|
+
invalidateGitHubLinkCachesForTarget({
|
|
8717
|
+
companyId,
|
|
8718
|
+
projectId,
|
|
8719
|
+
repositoryUrl: params.targetMapping.repositoryUrl
|
|
8720
|
+
});
|
|
8721
|
+
return {
|
|
8722
|
+
updatedDescriptionsCount: transferPatchResult.descriptionUpdated ? 1 : 0
|
|
8723
|
+
};
|
|
8724
|
+
}
|
|
8725
|
+
function buildUnmappedTransferredIssueCancellationComment(params) {
|
|
8726
|
+
return `GitHub Sync updated the status from \`${formatPaperclipIssueStatus(params.previousStatus)}\` to \`${formatPaperclipIssueStatus(params.nextStatus)}\` because the linked GitHub issue was transferred to \`${formatRepositoryLabel(params.transferredRepository)}\`, which is not mapped to a Paperclip project. The GitHub link was removed so sync will stop updating this Paperclip issue.`;
|
|
8727
|
+
}
|
|
8728
|
+
async function cancelUnmappedTransferredGitHubIssue(ctx, params) {
|
|
8729
|
+
const companyId = normalizeCompanyId(params.mapping.companyId);
|
|
8730
|
+
if (!companyId) {
|
|
8731
|
+
return {
|
|
8732
|
+
updatedStatusesCount: 0
|
|
8733
|
+
};
|
|
8734
|
+
}
|
|
8735
|
+
const paperclipIssue = await ctx.issues.get(params.importedIssue.paperclipIssueId, companyId);
|
|
8736
|
+
if (!paperclipIssue) {
|
|
8737
|
+
return {
|
|
8738
|
+
updatedStatusesCount: 0
|
|
8739
|
+
};
|
|
8740
|
+
}
|
|
8741
|
+
await unlinkPaperclipIssueFromGitHub(ctx, {
|
|
8742
|
+
companyId,
|
|
8743
|
+
issueId: params.importedIssue.paperclipIssueId
|
|
8744
|
+
});
|
|
8745
|
+
const nextStatus = "cancelled";
|
|
8746
|
+
await updatePaperclipIssueState(ctx, {
|
|
8747
|
+
companyId,
|
|
8748
|
+
issueId: params.importedIssue.paperclipIssueId,
|
|
8749
|
+
currentStatus: paperclipIssue.status,
|
|
8750
|
+
syncContext: getPaperclipIssueSyncContext(paperclipIssue),
|
|
8751
|
+
nextStatus,
|
|
8752
|
+
transitionComment: buildUnmappedTransferredIssueCancellationComment({
|
|
8753
|
+
previousStatus: paperclipIssue.status,
|
|
8754
|
+
nextStatus,
|
|
8755
|
+
transferredRepository: params.transferredRepository
|
|
8756
|
+
}),
|
|
8757
|
+
paperclipApiBaseUrl: params.paperclipApiBaseUrl
|
|
8758
|
+
});
|
|
8759
|
+
return {
|
|
8760
|
+
updatedStatusesCount: paperclipIssue.status === nextStatus ? 0 : 1
|
|
8761
|
+
};
|
|
8762
|
+
}
|
|
8763
|
+
async function fetchTransferredGitHubIssueForImportedRecord(octokit, repository, importedIssue) {
|
|
8764
|
+
if (importedIssue.githubIssueNumber === void 0) {
|
|
8765
|
+
return null;
|
|
8766
|
+
}
|
|
8767
|
+
const response = await octokit.rest.issues.get({
|
|
8768
|
+
owner: repository.owner,
|
|
8769
|
+
repo: repository.repo,
|
|
8770
|
+
issue_number: importedIssue.githubIssueNumber,
|
|
8771
|
+
headers: {
|
|
8772
|
+
accept: "application/vnd.github+json",
|
|
8773
|
+
"X-GitHub-Api-Version": GITHUB_API_VERSION
|
|
8774
|
+
}
|
|
8775
|
+
});
|
|
8776
|
+
const rawIssue = response.data;
|
|
8777
|
+
if (rawIssue.pull_request) {
|
|
8778
|
+
return null;
|
|
8779
|
+
}
|
|
8780
|
+
const githubIssue = normalizeGitHubIssueRecord(rawIssue);
|
|
8781
|
+
const transferredIssueReference = getGitHubIssueRepositoryReference(githubIssue);
|
|
8782
|
+
const transferredRepository = transferredIssueReference ? parseRepositoryReference(transferredIssueReference.repositoryUrl) : void 0;
|
|
8783
|
+
if (!transferredRepository || areRepositoriesEqual(repository, transferredRepository)) {
|
|
8784
|
+
return null;
|
|
8785
|
+
}
|
|
8786
|
+
return {
|
|
8787
|
+
githubIssue,
|
|
8788
|
+
transferredRepository
|
|
8789
|
+
};
|
|
8790
|
+
}
|
|
8791
|
+
async function reconcileTransferredImportedIssues(ctx, params) {
|
|
8792
|
+
let updatedStatusesCount = 0;
|
|
8793
|
+
let updatedDescriptionsCount = 0;
|
|
8794
|
+
for (const importedIssue of params.importedIssues) {
|
|
8795
|
+
if (params.allIssuesById.has(importedIssue.githubIssueId)) {
|
|
8796
|
+
continue;
|
|
8797
|
+
}
|
|
8798
|
+
try {
|
|
8799
|
+
updateSyncFailureContext(params.syncFailureContext, {
|
|
8800
|
+
phase: "evaluating_github_status",
|
|
8801
|
+
repositoryUrl: params.sourceRepository.url,
|
|
8802
|
+
githubIssueNumber: importedIssue.githubIssueNumber
|
|
8803
|
+
});
|
|
8804
|
+
const transfer = await fetchTransferredGitHubIssueForImportedRecord(
|
|
8805
|
+
params.octokit,
|
|
8806
|
+
params.sourceRepository,
|
|
8807
|
+
importedIssue
|
|
8808
|
+
);
|
|
8809
|
+
if (!transfer) {
|
|
8810
|
+
continue;
|
|
8811
|
+
}
|
|
8812
|
+
const targetMapping = findTransferredIssueTargetMapping(params.allMappings, {
|
|
8813
|
+
companyId: params.sourceMapping.companyId,
|
|
8814
|
+
repositoryUrl: transfer.transferredRepository.url
|
|
8815
|
+
});
|
|
8816
|
+
if (targetMapping) {
|
|
8817
|
+
const moveResult = await moveImportedIssueToTransferredGitHubMapping(ctx, {
|
|
8818
|
+
sourceMapping: params.sourceMapping,
|
|
8819
|
+
targetMapping,
|
|
8820
|
+
importedIssue,
|
|
8821
|
+
githubIssue: transfer.githubIssue,
|
|
8822
|
+
nextRegistry: params.nextRegistry,
|
|
8823
|
+
paperclipApiBaseUrl: params.paperclipApiBaseUrl
|
|
8824
|
+
});
|
|
8825
|
+
updatedDescriptionsCount += moveResult.updatedDescriptionsCount;
|
|
8826
|
+
continue;
|
|
8827
|
+
}
|
|
8828
|
+
const cancelResult = await cancelUnmappedTransferredGitHubIssue(ctx, {
|
|
8829
|
+
mapping: params.sourceMapping,
|
|
8830
|
+
importedIssue,
|
|
8831
|
+
transferredRepository: transfer.transferredRepository,
|
|
8832
|
+
paperclipApiBaseUrl: params.paperclipApiBaseUrl
|
|
8833
|
+
});
|
|
8834
|
+
for (let index = params.nextRegistry.length - 1; index >= 0; index -= 1) {
|
|
8835
|
+
const entry = params.nextRegistry[index];
|
|
8836
|
+
if (entry?.paperclipIssueId === importedIssue.paperclipIssueId && (!entry.companyId || entry.companyId === params.sourceMapping.companyId)) {
|
|
8837
|
+
params.nextRegistry.splice(index, 1);
|
|
8838
|
+
}
|
|
8839
|
+
}
|
|
8840
|
+
updatedStatusesCount += cancelResult.updatedStatusesCount;
|
|
8841
|
+
} catch (error) {
|
|
8842
|
+
if (isGitHubRateLimitError(error)) {
|
|
8843
|
+
throw error;
|
|
8844
|
+
}
|
|
8845
|
+
recordRecoverableSyncFailure(ctx, params.failures, error, params.syncFailureContext);
|
|
8846
|
+
continue;
|
|
8847
|
+
}
|
|
8848
|
+
}
|
|
8849
|
+
return {
|
|
8850
|
+
updatedStatusesCount,
|
|
8851
|
+
updatedDescriptionsCount
|
|
8852
|
+
};
|
|
8853
|
+
}
|
|
8453
8854
|
async function synchronizePaperclipIssueStatuses(ctx, octokit, repository, mapping, advancedSettings, allIssuesById, importedIssues, createdIssueIds, availableLabels, paperclipApiBaseUrl, linkedPullRequestsByIssueNumber, issueStatusSnapshotCache, pullRequestStatusCache, repositoryMaintainerCache, syncFailureContext, failures, assertNotCancelled, onGitHubIssueClosed, onProgress) {
|
|
8454
8855
|
if (!mapping.companyId || !ctx.issues || typeof ctx.issues.get !== "function" || typeof ctx.issues.update !== "function") {
|
|
8455
8856
|
return {
|
|
@@ -8638,7 +9039,13 @@ async function synchronizePaperclipIssueStatuses(ctx, octokit, repository, mappi
|
|
|
8638
9039
|
nextStatus,
|
|
8639
9040
|
syncContext: paperclipIssueSyncContext
|
|
8640
9041
|
});
|
|
8641
|
-
const
|
|
9042
|
+
const shouldPreserveImportedTriageRouting = shouldPreserveImportedTriageAssignee({
|
|
9043
|
+
currentStatus: paperclipIssue.status,
|
|
9044
|
+
nextStatus,
|
|
9045
|
+
wasImportedThisRun,
|
|
9046
|
+
maintainerAuthoredImportedIssue
|
|
9047
|
+
});
|
|
9048
|
+
const nextTransitionAssignee = shouldPreserveImportedTriageRouting ? null : resolveSyncTransitionAssignee({
|
|
8642
9049
|
currentStatus: paperclipIssue.status,
|
|
8643
9050
|
nextStatus,
|
|
8644
9051
|
syncContext: paperclipIssueSyncContext,
|
|
@@ -8646,7 +9053,7 @@ async function synchronizePaperclipIssueStatuses(ctx, octokit, repository, mappi
|
|
|
8646
9053
|
});
|
|
8647
9054
|
const shouldClearTransitionAssignee = nextStatus === "in_review" && (nextTransitionAssignee === null || shouldPreserveMaintainerWaitRouting) && paperclipIssueSyncContext.assignee !== null;
|
|
8648
9055
|
const nextAssigneeChanged = nextTransitionAssignee ? !doesPaperclipIssueAssigneeMatch(paperclipIssueSyncContext.assignee, nextTransitionAssignee.principal) : false;
|
|
8649
|
-
const shouldWakeImportedAssignee = wasImportedThisRun && paperclipIssue.status === nextStatus
|
|
9056
|
+
const shouldWakeImportedAssignee = wasImportedThisRun && nextStatus === "todo" && (paperclipIssue.status === nextStatus || shouldPreserveImportedTriageRouting) && paperclipIssueSyncContext.assignee?.kind === "agent";
|
|
8650
9057
|
const shouldWakeTransitionAssignee = paperclipIssue.status !== nextStatus && nextTransitionAssignee?.principal.kind === "agent" && isActionablePaperclipIssueStatus(nextStatus) && (nextAssigneeChanged || paperclipIssue.status !== nextStatus);
|
|
8651
9058
|
importedIssue.githubIssueNumber = githubIssue.number;
|
|
8652
9059
|
importedIssue.lastSeenCommentCount = snapshot.commentCount;
|
|
@@ -8708,6 +9115,14 @@ async function synchronizePaperclipIssueStatuses(ctx, octokit, repository, mappi
|
|
|
8708
9115
|
paperclipApiBaseUrl
|
|
8709
9116
|
});
|
|
8710
9117
|
updatedStatusesCount += 1;
|
|
9118
|
+
if (shouldWakeImportedAssignee) {
|
|
9119
|
+
queuedIssueWakeups.push({
|
|
9120
|
+
assigneeAgentId: paperclipIssueSyncContext.assignee?.kind === "agent" ? paperclipIssueSyncContext.assignee.id : null,
|
|
9121
|
+
paperclipIssueId: importedIssue.paperclipIssueId,
|
|
9122
|
+
reason: IMPORTED_ISSUE_WAKE_REASON,
|
|
9123
|
+
mutation: "import"
|
|
9124
|
+
});
|
|
9125
|
+
}
|
|
8711
9126
|
if (shouldWakeTransitionAssignee && nextTransitionAssignee?.principal.kind === "agent") {
|
|
8712
9127
|
queuedIssueWakeups.push({
|
|
8713
9128
|
assigneeAgentId: nextTransitionAssignee.principal.id,
|
|
@@ -12723,6 +13138,22 @@ async function performSync(ctx, trigger, options = {}) {
|
|
|
12723
13138
|
await persistRunningProgress(issueIndex === issues.length - 1);
|
|
12724
13139
|
}
|
|
12725
13140
|
}
|
|
13141
|
+
const transferReconciliationResult = await reconcileTransferredImportedIssues(ctx, {
|
|
13142
|
+
octokit,
|
|
13143
|
+
sourceMapping: mapping,
|
|
13144
|
+
sourceRepository: repository,
|
|
13145
|
+
allMappings: settings.mappings,
|
|
13146
|
+
importedIssues: [...importRegistryByIssueId.values()].filter(
|
|
13147
|
+
(importedIssue) => doesImportedIssueMatchTarget(importedIssue, options.target)
|
|
13148
|
+
),
|
|
13149
|
+
allIssuesById,
|
|
13150
|
+
nextRegistry,
|
|
13151
|
+
paperclipApiBaseUrl,
|
|
13152
|
+
syncFailureContext: failureContext,
|
|
13153
|
+
failures: recoverableFailures
|
|
13154
|
+
});
|
|
13155
|
+
updatedStatusesCount += transferReconciliationResult.updatedStatusesCount;
|
|
13156
|
+
updatedDescriptionsCount += transferReconciliationResult.updatedDescriptionsCount;
|
|
12726
13157
|
const importedIssuesForSynchronization = [...importRegistryByIssueId.values()].filter(
|
|
12727
13158
|
(importedIssue) => allIssuesById.has(importedIssue.githubIssueId) && doesImportedIssueMatchTarget(importedIssue, options.target)
|
|
12728
13159
|
);
|
|
@@ -14008,6 +14439,18 @@ var plugin = definePlugin({
|
|
|
14008
14439
|
requireUnlinked: true
|
|
14009
14440
|
});
|
|
14010
14441
|
});
|
|
14442
|
+
ctx.actions.register("issue.unlinkGitHubItem", async (input) => {
|
|
14443
|
+
const record = input && typeof input === "object" ? input : {};
|
|
14444
|
+
const companyId = normalizeCompanyId(record.companyId);
|
|
14445
|
+
const issueId = normalizeOptionalString2(record.issueId);
|
|
14446
|
+
if (!companyId || !issueId) {
|
|
14447
|
+
throw new Error("companyId and issueId are required.");
|
|
14448
|
+
}
|
|
14449
|
+
return unlinkPaperclipIssueFromGitHub(ctx, {
|
|
14450
|
+
companyId,
|
|
14451
|
+
issueId
|
|
14452
|
+
});
|
|
14453
|
+
});
|
|
14011
14454
|
ctx.actions.register("settings.saveRegistration", async (input) => {
|
|
14012
14455
|
const previous = normalizeSettings(await ctx.state.get(SETTINGS_SCOPE));
|
|
14013
14456
|
const config = await getResolvedConfig(ctx);
|