mirror of
https://github.com/yu-i-i/overleaf-cep.git
synced 2026-11-12 13:13:45 +01:00
Changes by @fporrmann
Added missing return in clean sync flow Fixed incorrect initialization order Rewrote commit comparison for merge state evaluation Fixed a problem with the getBlobContent function Removed old log lines Fixed merging when there were changes in Gitlab and Overleaf Removed additional log line
This commit is contained in:
@@ -193,6 +193,19 @@ function getTokenRefreshTimestamp(token, safetyMarginInSec = 300) {
|
||||
return token.created_at + token.expires_in - safetyMarginInSec;
|
||||
}
|
||||
|
||||
|
||||
async function compareCommitsFull(token, repoFullName, from, to) {
|
||||
const url = new URL(`${GITLAB_API_BASE}/projects/${projectPath(repoFullName)}/repository/compare`)
|
||||
url.searchParams.set('from', from)
|
||||
url.searchParams.set('to', to)
|
||||
|
||||
return await fetchGitLabJson(url.toString(), {
|
||||
headers: buildHeaders(token),
|
||||
signal: AbortSignal.timeout(REQUEST_TIMEOUT_MS)
|
||||
}, 'compareCommitsFull')
|
||||
}
|
||||
|
||||
|
||||
// ---------------------- exports ------------------------------- //
|
||||
|
||||
// OAuth
|
||||
@@ -221,18 +234,18 @@ function exchangeCodeForToken(code) {
|
||||
}
|
||||
|
||||
function refreshToken(refreshToken) {
|
||||
return fetchGitLabJson(`${GITLAB_URL}/oauth/token`, {
|
||||
method: 'POST',
|
||||
headers: buildHeaders(),
|
||||
json: {
|
||||
client_id: Settings.gitlabSync.clientID,
|
||||
client_secret: Settings.gitlabSync.clientSecret,
|
||||
refresh_token: refreshToken,
|
||||
grant_type: 'refresh_token',
|
||||
redirect_uri: Settings.gitlabSync.callbackURL,
|
||||
},
|
||||
signal: AbortSignal.timeout(REQUEST_TIMEOUT_MS)
|
||||
}, 'refreshToken').then(r => [r.access_token, r.refresh_token, getTokenRefreshTimestamp(r)])
|
||||
return fetchGitLabJson(`${GITLAB_URL}/oauth/token`, {
|
||||
method: 'POST',
|
||||
headers: buildHeaders(),
|
||||
json: {
|
||||
client_id: Settings.gitlabSync.clientID,
|
||||
client_secret: Settings.gitlabSync.clientSecret,
|
||||
refresh_token: refreshToken,
|
||||
grant_type: 'refresh_token',
|
||||
redirect_uri: Settings.gitlabSync.callbackURL,
|
||||
},
|
||||
signal: AbortSignal.timeout(REQUEST_TIMEOUT_MS)
|
||||
}, 'refreshToken').then(r => [r.access_token, r.refresh_token, getTokenRefreshTimestamp(r)])
|
||||
}
|
||||
|
||||
function revokeToken(token) {
|
||||
@@ -405,13 +418,13 @@ function createCommit(token, repoFullName, { tree, message, branch, start_sha, f
|
||||
const actions = Array.isArray(tree?.entries) ? tree.entries : []
|
||||
|
||||
let payload = {
|
||||
branch: branch || GITLAB_DEFAULT_BRANCH,
|
||||
commit_message: message,
|
||||
actions,
|
||||
branch: branch || GITLAB_DEFAULT_BRANCH,
|
||||
commit_message: message,
|
||||
actions,
|
||||
}
|
||||
|
||||
if (branch && branch != GITLAB_DEFAULT_BRANCH) {
|
||||
payload.start_sha = start_sha
|
||||
payload.start_sha = start_sha
|
||||
}
|
||||
|
||||
return fetchGitLabJson(`${GITLAB_API_BASE}/projects/${projectPath(repoFullName)}/repository/commits`, {
|
||||
@@ -448,31 +461,40 @@ async function listBlobsAtCommit(token, repoFullName, commit) {
|
||||
}
|
||||
|
||||
async function getBlobContent(token, repoFullName, sha) {
|
||||
if (!sha) return null
|
||||
|
||||
const url = `${GITLAB_API_BASE}/projects/${projectPath(repoFullName)}/repository/blobs/${sha}`
|
||||
|
||||
try {
|
||||
const json = await fetchGitLabJson(url, {
|
||||
headers: buildHeaders(token),
|
||||
signal: AbortSignal.timeout(REQUEST_LONG_TIMEOUT_MS)
|
||||
}, 'getBlobContent')
|
||||
return json.content
|
||||
} catch (err) {
|
||||
throw OError.tag(err, 'Failed to fetch blob content from GitLab', { repoFullName, sha })
|
||||
}
|
||||
return await fetchGitLabJson(url, {
|
||||
headers: buildHeaders(token),
|
||||
signal: AbortSignal.timeout(REQUEST_LONG_TIMEOUT_MS)
|
||||
}, 'getBlobContent').then(r => Buffer.from(r.content, 'base64'))
|
||||
}
|
||||
|
||||
async function listNewCommitsWithStatus(token, fullName, branchName, fromCommit) {
|
||||
const url = new URL(`${GITLAB_API_BASE}/projects/${projectPath(fullName)}/repository/compare`)
|
||||
url.searchParams.set('from', fromCommit)
|
||||
url.searchParams.set('to', branchName)
|
||||
url.searchParams.set('straight', 'true')
|
||||
const forward = await compareCommitsFull(token, fullName, fromCommit, branchName);
|
||||
const reverse = await compareCommitsFull(token, fullName, branchName, fromCommit);
|
||||
const forwardHasCommits = forward.commits.length > 0;
|
||||
const reverseHasCommits = reverse.commits.length > 0;
|
||||
|
||||
const data = await fetchGitLabJson(url.toString(), {
|
||||
headers: buildHeaders(token),
|
||||
signal: AbortSignal.timeout(REQUEST_LONG_TIMEOUT_MS)
|
||||
}, 'listNewCommitsWithStatus')
|
||||
let diverged = false
|
||||
|
||||
const commits = (data.commits || []).map(c => ({
|
||||
if (forward.compare_same_ref) {
|
||||
// Identical
|
||||
}
|
||||
else if (forwardHasCommits && !reverseHasCommits) {
|
||||
// Ahead
|
||||
}
|
||||
else if (!forwardHasCommits && reverseHasCommits) {
|
||||
// Behind (here also considered a diverged state)
|
||||
diverged = true
|
||||
}
|
||||
else if (forwardHasCommits && reverseHasCommits) {
|
||||
// Diverged
|
||||
diverged = true
|
||||
}
|
||||
|
||||
const commits = (forward.commits || []).map(c => ({
|
||||
message: c.message || c.title || '',
|
||||
author: {
|
||||
name: c.author_name || '',
|
||||
@@ -482,7 +504,7 @@ async function listNewCommitsWithStatus(token, fullName, branchName, fromCommit)
|
||||
sha: c.id,
|
||||
}))
|
||||
|
||||
return { commits, diverged: commits.length > 0 }
|
||||
return { commits, diverged }
|
||||
}
|
||||
|
||||
// branches
|
||||
@@ -538,16 +560,16 @@ async function waitForMergeRequestToBeReady(token, repoFullName, mergeRequestIid
|
||||
|
||||
while (Date.now() - startedAt < MERGE_REQUEST_TIMEOUT_MS) {
|
||||
const mergeRequest = await fetchGitLabJson(`${GITLAB_API_BASE}/projects/${projectPath(repoFullName)}/merge_requests/${mergeRequestIid}`, {
|
||||
headers: buildHeaders(token),
|
||||
signal: AbortSignal.timeout(REQUEST_TIMEOUT_MS),
|
||||
}, 'getMergeRequest')
|
||||
headers: buildHeaders(token),
|
||||
signal: AbortSignal.timeout(REQUEST_TIMEOUT_MS),
|
||||
}, 'getMergeRequest')
|
||||
|
||||
const status = mergeRequest.detailed_merge_status
|
||||
|
||||
if (status === 'mergeable') {
|
||||
return {
|
||||
canMerge: true,
|
||||
reason: status,
|
||||
reason: status,
|
||||
}
|
||||
}
|
||||
|
||||
@@ -584,13 +606,13 @@ async function mergeOpenMergeRequest(token, repoFullName, mergeRequestIid) {
|
||||
function mergeBranch(token, repoFullName, base, head) {
|
||||
return createMergeRequest(token, repoFullName, head, base, `Merge ${head} into ${base}`)
|
||||
.then(async mergeRequest => {
|
||||
const ret = await waitForMergeRequestToBeReady(token, repoFullName, mergeRequest.iid)
|
||||
if (!ret.canMerge) {
|
||||
const ret = await waitForMergeRequestToBeReady(token, repoFullName, mergeRequest.iid)
|
||||
if (!ret.canMerge) {
|
||||
throw new GitConflictError("Merge request cannot be merged due to conflicts, reason: " + ret.reason)
|
||||
}
|
||||
}
|
||||
|
||||
await mergeOpenMergeRequest(token, repoFullName, mergeRequest.iid)
|
||||
return getBranchHead(token, repoFullName, base)
|
||||
await mergeOpenMergeRequest(token, repoFullName, mergeRequest.iid)
|
||||
return getBranchHead(token, repoFullName, base)
|
||||
})
|
||||
}
|
||||
|
||||
@@ -613,6 +635,7 @@ function getRepoZipball(token, repoFullName, sha) {
|
||||
}, 'getRepoZipball')
|
||||
}
|
||||
|
||||
|
||||
export default {
|
||||
maxConcurrency,
|
||||
getOAuth2Url,
|
||||
|
||||
@@ -123,9 +123,9 @@ async function resolveCleanSyncState(
|
||||
return null
|
||||
}
|
||||
|
||||
try
|
||||
{
|
||||
// Possible changes in OL
|
||||
// Possible changes in OL
|
||||
if (defaultBranchHead === projectSyncState.lastSyncCommit) {
|
||||
try {
|
||||
const olBranchHead =
|
||||
await exportChangesToGit({
|
||||
token,
|
||||
@@ -135,55 +135,41 @@ async function resolveCleanSyncState(
|
||||
currentVersion,
|
||||
message,
|
||||
baseCommit: projectSyncState.lastSyncCommit,
|
||||
defaultBranchName,
|
||||
})
|
||||
|
||||
if (!olBranchHead) {
|
||||
if ( defaultBranchHead === projectSyncState.lastSyncCommit) {
|
||||
await SyncStateManager.updateProjectState(projectId, { lastSyncVersion: currentVersion })
|
||||
return null
|
||||
}
|
||||
|
||||
const lastSyncCommit = defaultBranchHead
|
||||
const lastSyncVersion = await applyGitSnapshotToProject({
|
||||
token,
|
||||
userId,
|
||||
projectId,
|
||||
repoFullName,
|
||||
lastSyncCommit,
|
||||
})
|
||||
await SyncStateManager.updateProjectState(projectId, { lastSyncVersion, lastSyncCommit })
|
||||
if (!olBranchHead) { // no real canges in OL, but currentVersion is ahead
|
||||
await SyncStateManager.updateProjectState(projectId, { lastSyncVersion: currentVersion })
|
||||
return null
|
||||
}
|
||||
const lastSyncCommit = olBranchHead
|
||||
const lastSyncVersion = await applyGitSnapshotToProject({
|
||||
token,
|
||||
userId,
|
||||
projectId,
|
||||
repoFullName,
|
||||
lastSyncCommit,
|
||||
})
|
||||
|
||||
if (defaultBranchHead === projectSyncState.lastSyncCommit) {
|
||||
const lastSyncCommit = olBranchHead
|
||||
const lastSyncVersion = await applyGitSnapshotToProject({
|
||||
token,
|
||||
userId,
|
||||
projectId,
|
||||
repoFullName,
|
||||
lastSyncCommit,
|
||||
})
|
||||
await SyncStateManager.updateProjectState(projectId, { lastSyncVersion, lastSyncCommit })
|
||||
}
|
||||
}
|
||||
catch (err) {
|
||||
logger.error({ err, projectId }, 'Error during clean sync state resolution, fallback to merge')
|
||||
}
|
||||
await SyncStateManager.updateProjectState(projectId, { lastSyncVersion, lastSyncCommit })
|
||||
return null
|
||||
} catch (err) {
|
||||
logger.error({ err, projectId }, 'Error during clean sync state resolution, fallback to merge')
|
||||
}
|
||||
}
|
||||
|
||||
// Normal commit failed, fallback to merge, first create a temporary branch with OL changes
|
||||
// Git head is ahead of last known sync commit, create a temporary branch with OL changes and perform a merge with the default branch
|
||||
const tempBranchName = generateBranchName()
|
||||
const olBranchHead =
|
||||
await exportChangesToGit({
|
||||
token,
|
||||
projectId,
|
||||
repoFullName,
|
||||
lastSyncVersion: projectSyncState.lastSyncVersion,
|
||||
currentVersion,
|
||||
message,
|
||||
baseCommit: projectSyncState.lastSyncCommit,
|
||||
branch: tempBranchName,
|
||||
})
|
||||
await exportChangesToGit({
|
||||
token,
|
||||
projectId,
|
||||
repoFullName,
|
||||
lastSyncVersion: projectSyncState.lastSyncVersion,
|
||||
currentVersion,
|
||||
message,
|
||||
baseCommit: projectSyncState.lastSyncCommit,
|
||||
branch: tempBranchName,
|
||||
})
|
||||
|
||||
// Merge the temporary branch into the default branch
|
||||
const { mergeCommit, conflict } = await mergeWithTempBranch(
|
||||
@@ -293,7 +279,7 @@ async function resolveConflictSyncState(
|
||||
repoFullName,
|
||||
defaultBranchName,
|
||||
newOlBranchHead,
|
||||
tempBranchName,
|
||||
tempBranchName,
|
||||
)
|
||||
|
||||
if (conflict) {
|
||||
@@ -455,7 +441,7 @@ async function resolveDetachedSyncState(
|
||||
parentCommit: baseCommit,
|
||||
entries: [...cleanLocalEntries, ...conflictLocalEntries],
|
||||
message,
|
||||
branch: tempBranchName,
|
||||
branch: tempBranchName,
|
||||
})
|
||||
|
||||
// merge: default <- temp (GH <- OL)
|
||||
@@ -621,7 +607,7 @@ async function createCommitFromEntries({
|
||||
tree: newTree,
|
||||
start_sha: parentCommit,
|
||||
message,
|
||||
branch,
|
||||
branch,
|
||||
})
|
||||
}
|
||||
|
||||
@@ -632,7 +618,8 @@ async function mergeWithTempBranch(
|
||||
tempBranchName
|
||||
) {
|
||||
try {
|
||||
const mergeCommit = await api.mergeBranch(token, repoFullName, branchName, tempBranchName)
|
||||
const mergeCommit =
|
||||
await api.mergeBranch(token, repoFullName, branchName, tempBranchName)
|
||||
|
||||
return { conflict: false, mergeCommit }
|
||||
|
||||
@@ -713,7 +700,7 @@ async function exportChangesToGit({
|
||||
parentCommit: baseCommit,
|
||||
entries,
|
||||
message,
|
||||
branch,
|
||||
branch,
|
||||
})
|
||||
|
||||
return newCommit
|
||||
|
||||
Reference in New Issue
Block a user