fix(sync): use stable dedup key and skip threads when head unresolved
This commit is contained in:
@@ -16,6 +16,7 @@ import {
|
||||
listPullRequests,
|
||||
syncForkBranch,
|
||||
} from './githubClient';
|
||||
import { maintenanceDedupKey } from './maintenanceDedup';
|
||||
import { shouldAutoSync, shouldCreateReviewThread } from './syncGating';
|
||||
|
||||
const getRequiredUserId = async (ctx: ActionCtx) => {
|
||||
@@ -244,6 +245,32 @@ const refreshOwnedSpoon = async (
|
||||
} catch (syncError) {
|
||||
const message =
|
||||
syncError instanceof Error ? syncError.message : String(syncError);
|
||||
const dedupKey = maintenanceDedupKey({
|
||||
mergeBaseSha: upstreamCompare.mergeBaseSha ?? forkCompare.mergeBaseSha,
|
||||
headSha: upstreamCompare.headSha,
|
||||
});
|
||||
if (!dedupKey) {
|
||||
console.warn(
|
||||
'Skipping maintenance thread: upstream head SHA unresolvable for spoon',
|
||||
spoonId,
|
||||
);
|
||||
await ctx.runMutation(internal.syncRuns.patchInternal, {
|
||||
syncRunId,
|
||||
status: 'failed',
|
||||
error: `${message} (skipped maintenance thread: could not resolve upstream head SHA)`,
|
||||
});
|
||||
await ctx.runMutation(internal.spoons.patchSyncFields, {
|
||||
spoonId,
|
||||
syncStatus: 'conflict',
|
||||
lastError: message,
|
||||
});
|
||||
return {
|
||||
success: true,
|
||||
status: 'unknown' as const,
|
||||
upstreamAheadBy: upstreamCompare.aheadBy,
|
||||
forkAheadBy: forkCompare.aheadBy,
|
||||
};
|
||||
}
|
||||
const threadId = await ctx.runMutation(
|
||||
internal.threads.createMaintenanceThread,
|
||||
{
|
||||
@@ -253,7 +280,8 @@ const refreshOwnedSpoon = async (
|
||||
title: `Resolve upstream sync conflict for ${spoon.name}`,
|
||||
summary: `GitHub refused the automatic upstream sync: ${message}`,
|
||||
upstreamFrom: upstreamCompare.mergeBaseSha,
|
||||
upstreamTo: upstreamCompare.headSha ?? `${Date.now()}`,
|
||||
upstreamTo: upstreamCompare.headSha,
|
||||
dedupKey,
|
||||
forkHeadAtCreation: forkCompare.headSha,
|
||||
mergeBaseAtCreation:
|
||||
upstreamCompare.mergeBaseSha ?? forkCompare.mergeBaseSha,
|
||||
@@ -283,28 +311,46 @@ const refreshOwnedSpoon = async (
|
||||
}
|
||||
|
||||
if (shouldCreateReviewThread({ autoReviewEnabled }, { status })) {
|
||||
const threadId = await ctx.runMutation(
|
||||
internal.threads.createMaintenanceThread,
|
||||
{
|
||||
spoonId,
|
||||
ownerId,
|
||||
source: 'upstream_update',
|
||||
title: `Review upstream changes for ${spoon.name}`,
|
||||
summary: `Upstream has ${upstreamCompare.aheadBy} commit(s) and the fork has ${forkCompare.aheadBy} custom commit(s). Review whether upstream should be merged, ignored, or resolved in a draft PR.`,
|
||||
upstreamFrom: upstreamCompare.mergeBaseSha,
|
||||
upstreamTo: upstreamCompare.headSha ?? `${Date.now()}`,
|
||||
forkHeadAtCreation: forkCompare.headSha,
|
||||
mergeBaseAtCreation:
|
||||
upstreamCompare.mergeBaseSha ?? forkCompare.mergeBaseSha,
|
||||
relatedSyncRunId: syncRunId,
|
||||
jobType: 'maintenance_review',
|
||||
},
|
||||
);
|
||||
await ctx.runMutation(internal.syncRuns.patchInternal, {
|
||||
syncRunId,
|
||||
threadId,
|
||||
decision: 'thread_created',
|
||||
const dedupKey = maintenanceDedupKey({
|
||||
mergeBaseSha: upstreamCompare.mergeBaseSha ?? forkCompare.mergeBaseSha,
|
||||
headSha: upstreamCompare.headSha,
|
||||
});
|
||||
if (!dedupKey) {
|
||||
console.warn(
|
||||
'Skipping maintenance thread: upstream head SHA unresolvable for spoon',
|
||||
spoonId,
|
||||
);
|
||||
await ctx.runMutation(internal.syncRuns.patchInternal, {
|
||||
syncRunId,
|
||||
status: 'failed',
|
||||
error:
|
||||
'Could not resolve upstream head SHA; skipping maintenance thread.',
|
||||
});
|
||||
} else {
|
||||
const threadId = await ctx.runMutation(
|
||||
internal.threads.createMaintenanceThread,
|
||||
{
|
||||
spoonId,
|
||||
ownerId,
|
||||
source: 'upstream_update',
|
||||
title: `Review upstream changes for ${spoon.name}`,
|
||||
summary: `Upstream has ${upstreamCompare.aheadBy} commit(s) and the fork has ${forkCompare.aheadBy} custom commit(s). Review whether upstream should be merged, ignored, or resolved in a draft PR.`,
|
||||
upstreamFrom: upstreamCompare.mergeBaseSha,
|
||||
upstreamTo: upstreamCompare.headSha,
|
||||
dedupKey,
|
||||
forkHeadAtCreation: forkCompare.headSha,
|
||||
mergeBaseAtCreation:
|
||||
upstreamCompare.mergeBaseSha ?? forkCompare.mergeBaseSha,
|
||||
relatedSyncRunId: syncRunId,
|
||||
jobType: 'maintenance_review',
|
||||
},
|
||||
);
|
||||
await ctx.runMutation(internal.syncRuns.patchInternal, {
|
||||
syncRunId,
|
||||
threadId,
|
||||
decision: 'thread_created',
|
||||
});
|
||||
}
|
||||
}
|
||||
return {
|
||||
success: true,
|
||||
@@ -408,30 +454,39 @@ export const syncForkWithUpstream = action({
|
||||
const message = error instanceof Error ? error.message : String(error);
|
||||
const conflict = message.toLowerCase().includes('conflict');
|
||||
if (conflict) {
|
||||
const threadId = await ctx.runMutation(
|
||||
internal.threads.createMaintenanceThread,
|
||||
{
|
||||
spoonId,
|
||||
ownerId,
|
||||
source: 'merge_conflict',
|
||||
title: `Resolve upstream sync conflict for ${spoon.name}`,
|
||||
summary: `GitHub reported a conflict while syncing upstream into this fork: ${message}`,
|
||||
upstreamTo:
|
||||
state.upstreamHeadSha ??
|
||||
spoon.lastUpstreamCommit ??
|
||||
`${Date.now()}`,
|
||||
forkHeadAtCreation: state.forkHeadSha ?? spoon.lastForkCommit,
|
||||
mergeBaseAtCreation:
|
||||
state.mergeBaseSha ?? spoon.lastMergeBaseCommit,
|
||||
relatedSyncRunId: syncRunId,
|
||||
jobType: 'conflict_resolution',
|
||||
},
|
||||
);
|
||||
await ctx.runMutation(internal.syncRuns.patchInternal, {
|
||||
syncRunId,
|
||||
threadId,
|
||||
decision: 'thread_created',
|
||||
const dedupKey = maintenanceDedupKey({
|
||||
mergeBaseSha: state.mergeBaseSha ?? spoon.lastMergeBaseCommit,
|
||||
headSha: state.upstreamHeadSha ?? spoon.lastUpstreamCommit,
|
||||
});
|
||||
if (!dedupKey) {
|
||||
console.warn(
|
||||
'Skipping maintenance thread: upstream head SHA unresolvable for spoon',
|
||||
spoonId,
|
||||
);
|
||||
} else {
|
||||
const threadId = await ctx.runMutation(
|
||||
internal.threads.createMaintenanceThread,
|
||||
{
|
||||
spoonId,
|
||||
ownerId,
|
||||
source: 'merge_conflict',
|
||||
title: `Resolve upstream sync conflict for ${spoon.name}`,
|
||||
summary: `GitHub reported a conflict while syncing upstream into this fork: ${message}`,
|
||||
upstreamTo: state.upstreamHeadSha ?? spoon.lastUpstreamCommit,
|
||||
dedupKey,
|
||||
forkHeadAtCreation: state.forkHeadSha ?? spoon.lastForkCommit,
|
||||
mergeBaseAtCreation:
|
||||
state.mergeBaseSha ?? spoon.lastMergeBaseCommit,
|
||||
relatedSyncRunId: syncRunId,
|
||||
jobType: 'conflict_resolution',
|
||||
},
|
||||
);
|
||||
await ctx.runMutation(internal.syncRuns.patchInternal, {
|
||||
syncRunId,
|
||||
threadId,
|
||||
decision: 'thread_created',
|
||||
});
|
||||
}
|
||||
}
|
||||
await ctx.runMutation(internal.syncRuns.patchInternal, {
|
||||
syncRunId,
|
||||
|
||||
Reference in New Issue
Block a user