Skip to content

Feature: GitHub sync#27

Open
Musicminion wants to merge 25 commits intoserver-profrom
feat-github-sync
Open

Feature: GitHub sync#27
Musicminion wants to merge 25 commits intoserver-profrom
feat-github-sync

Conversation

@Musicminion
Copy link
Member

@Musicminion Musicminion commented Feb 24, 2026

Description

We Need Github sync, this is a Feature we have been asked for a long time, but need paid on overleaf, not available for server-pro.

Todo

Env Var:

# Access token encryption settings
# used for encryptionn of github accesstoken
CIPHER_PASSWORD=0123456789ABCDEFG 

# Github Sync Settings
GITHUB_SYNC_ENABLED=true
GITHUB_SYNC_CLIENT_ID=xxxxx
GITHUB_SYNC_CLIENT_SECRET=xxxxxx
GITHUB_SYNC_CALLBACK_URL=http://localhost/github-sync/completeRegistration

Demo

image image image image

Related issues / Pull Requests

Contributor Agreement

Copy link

@chatgpt-codex-connector chatgpt-codex-connector bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

💡 Codex Review

Here are some automated review suggestions for this pull request.

Reviewed commit: fa29536aa2

ℹ️ About Codex in GitHub

Your team has set up Codex to review pull requests in this repo. Reviews are triggered when you

  • Open a pull request for review
  • Mark a draft as ready
  • Comment "@codex review".

If Codex has suggestions, it will comment; otherwise it will react with 👍.

Codex can also answer questions or update the PR. Try commenting "@codex address that feedback".

Comment on lines +176 to +178
async function exportProject(req, res){

}

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

P1 Badge Implement response logic in routed GitHub sync actions

These handlers are mounted in GitHubSyncRouter (/project/:Project_id/github-sync/export, /commits/unmerged, and /merge), but they currently do nothing and never send a response or call next, so requests to these endpoints will hang until timeout instead of returning success/error.

Useful? React with 👍 / 👎.

Comment on lines +69 to +72
const user = await User.findById(userId, 'githubSync.tokenEncrypted').lean()

if (!user?.githubSync?.tokenEncrypted) {
return null

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

P1 Badge Read OAuth token from credentials collection for sync ops

OAuth linking stores tokens in GitHubSyncUserCredentials via saveGitHubAccessTokenForUser, but getUserPat reads User.githubSync.tokenEncrypted instead; as a result, downstream sync paths that depend on getUserPat (configure/branch listing/push) will report "GitHub not connected" even after a successful OAuth registration.

Useful? React with 👍 / 👎.

)

const ab = await response.arrayBuffer()
fs.writeFileSync(fsPath, Buffer.from(ab))

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

P2 Badge Remove temporary GitHub archive after import finishes

The imported repository zip is written to Settings.path.dumpFolder but never deleted in either success or error paths, so each import leaves a file behind and will steadily consume disk space on the web service over time.

Useful? React with 👍 / 👎.

- add reminder if user has not authorized github
- add readme for API document
- fix repeated repo in github error display
- add API for checking unmerged commits in github, with frontend dispaly
- add commit message table in modal (ToDo: backend code)
- remove sync status for delete project (hard del)
- remove branch for unneed sync
- add GITHUB_SYNC_ENABLED to control module import
- update README with Overleaf Pro features and add new screenshot
- update GitHub Sync README
@Musicminion
Copy link
Member Author

@codex, please review code

Copy link

@chatgpt-codex-connector chatgpt-codex-connector bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

💡 Codex Review

Here are some automated review suggestions for this pull request.

Reviewed commit: 17c0e85666

ℹ️ About Codex in GitHub

Your team has set up Codex to review pull requests in this repo. Reviews are triggered when you

  • Open a pull request for review
  • Mark a draft as ready
  • Comment "@codex review".

If Codex has suggestions, it will comment; otherwise it will react with 👍.

Codex can also answer questions or update the PR. Try commenting "@codex address that feedback".

Comment on lines +677 to +680
if (!response.ok) {
const errorData = await response.json().catch(() => ({}))
logger.error('Failed to diff branches on GitHub', { repoFullName, baseBranch, compareBranch, error: errorData })
return []

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

P1 Badge Propagate compare API failures in branch-diff check

When the GitHub compare call fails (for example token revocation, rate limiting, or transient 5xx), this code logs and returns an empty diff, and mergeToGitHubAndPushback treats an empty diff as “conflict resolved”. That can incorrectly advance sync state as successful even though the branch comparison never succeeded, leaving unresolved changes silently ignored.

Useful? React with 👍 / 👎.

Comment on lines +116 to +120
let gitHubSyncUserCredentials = new GitHubSyncUserCredentials()
gitHubSyncUserCredentials.userId = userId
gitHubSyncUserCredentials.auth_token_encrypted = tokenEncrypted

// save tp database

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

P1 Badge Upsert GitHub credentials instead of always inserting

OAuth completion always creates and saves a new GitHubSyncUserCredentials document for the user; on a second linking attempt for the same account this can fail with a duplicate-key error (or create duplicate rows where indexes are missing), so re-linking after token problems can break instead of rotating the stored token.

Useful? React with 👍 / 👎.

Comment on lines +277 to +280
} catch (err) {
res.status(500).json({ error: err.message })
}
res.status(200).json({ message: 'Merge to GitHub and push back process completed' })

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

P2 Badge Return after sending the merge error response

In the outer catch, a 500 response is written but execution continues to the trailing 200 response send, which triggers “headers already sent” on any thrown error path. This obscures the original failure and can produce noisy runtime errors during sync failures.

Useful? React with 👍 / 👎.

Comment on lines +1102 to +1105
Path.resolve(
__dirname,
'../modules/github-sync/frontend/js/components/github-integration-card.tsx'
),

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

P2 Badge Gate GitHub integration card behind feature enablement

The GitHub integration UI is registered unconditionally here, but the backend router is only mounted when GITHUB_SYNC_ENABLED is true (modules/github-sync/index.mjs). On deployments with the flag off, users still see the card and hit missing endpoints (or a stuck loading modal), so the disable flag does not actually disable the user-facing feature.

Useful? React with 👍 / 👎.

@Musicminion
Copy link
Member Author

@codex, please review code.

ps: We don't mind if github-sync is displayed in the frontend, we only remove backend code if not needed.

@Musicminion Musicminion self-assigned this Mar 3, 2026
Copy link

@chatgpt-codex-connector chatgpt-codex-connector bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

💡 Codex Review

Here are some automated review suggestions for this pull request.

Reviewed commit: 0886af21c0

ℹ️ About Codex in GitHub

Your team has set up Codex to review pull requests in this repo. Reviews are triggered when you

  • Open a pull request for review
  • Mark a draft as ready
  • Comment "@codex review".

If Codex has suggestions, it will comment; otherwise it will react with 👍.

Codex can also answer questions or update the PR. Try commenting "@codex address that feedback".

throw new Error(`GitHub API error: ${errorData.message || response.statusText}`)
}

const mergeData = await response.json()

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

P1 Badge Handle 204 merge responses without JSON parsing

mergeBranchToDefaultBranch unconditionally parses the merge response body as JSON, but GitHub returns 204 No Content when the head is already merged into the base branch. In that case response.json() throws, and mergeToGitHubAndPushback treats it as a merge failure (setting merge_status: 'failure' and unmerged_branch), so users can be incorrectly pushed into a conflict state even though no conflict exists.

Useful? React with 👍 / 👎.

Copy link
Member Author

@Musicminion Musicminion Mar 3, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Not possible, we have just called createOrUpdateBranchRef to create a new branch, then, exportChangesToGitHub, now, this branch can be merged absolutely. It can't be merged in the past,

// test if the token is still valid by making an API call to GitHub
const token = await SecretsHelper.decryptAccessToken(credentials.auth_token_encrypted)
try {
await GitHubApiClient.listRepos(token, { per_page: 1 })

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

P2 Badge Pass numeric pagination args when checking token validity

getUserGitHubStatus calls GitHubApiClient.listRepos(token, { per_page: 1 }), but listRepos expects positional numeric arguments (pat, page, perPage). This sends page=[object Object] and leaves per_page at its default 100, which can make /user/github-sync/status checks slower and can misclassify valid tokens as invalid if GitHub rejects the malformed page parameter.

Useful? React with 👍 / 👎.

Comment on lines +769 to +772
for (const item of tree) {
let obj = {
name: item.path,
}

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

P2 Badge Filter non-blob tree entries from generated sync file list

generateRespURL emits every node from the Git tree, including directory (type: "tree") entries, as if they were files. The web updater later treats each name as a present file path when computing deletions, so file-to-directory transitions (e.g., foo becoming foo/bar) can leave stale file paths undeleted and cause path conflicts during apply.

Useful? React with 👍 / 👎.

@Musicminion
Copy link
Member Author

@codex, review code again

Copy link

@chatgpt-codex-connector chatgpt-codex-connector bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

💡 Codex Review

Here are some automated review suggestions for this pull request.

Reviewed commit: bc23e17936

ℹ️ About Codex in GitHub

Your team has set up Codex to review pull requests in this repo. Reviews are triggered when you

  • Open a pull request for review
  • Mark a draft as ready
  • Comment "@codex review".

If Codex has suggestions, it will comment; otherwise it will react with 👍.

Codex can also answer questions or update the PR. Try commenting "@codex address that feedback".

Comment on lines +289 to +292
if (!projectStatus) {
return res.status(400).json({ error: 'Project is not linked to a GitHub repository' })
}
const ownerId = projectStatus.ownerId

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

P1 Badge Check project sync enabled before merge flow

getProjectGitHubSyncStatus returns { enabled: false } for unlinked projects, so this if (!projectStatus) guard never fires and the code proceeds with ownerId as undefined. In that case /project/:Project_id/github-sync/merge calls the github-sync service with an invalid user id and returns a 500 instead of the expected “project not linked” client error, which makes unlinked projects hit a broken merge path.

Useful? React with 👍 / 👎.

Comment on lines +681 to +683
// treat 404 as no diff.
if (errorData.status === '404') {
return []

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

P2 Badge Detect HTTP 404 using response status

The fallback for deleted unmerged branches is intended to treat a compare 404 as “no diff,” but this check inspects errorData.status === '404' instead of response.status. GitHub’s error payload does not reliably provide this string field, so a real 404 still throws and keeps conflict recovery failing even after the branch is removed on GitHub.

Useful? React with 👍 / 👎.

Comment on lines +1018 to +1022
integrationLinkingWidgets: [
Path.resolve(
__dirname,
'../modules/github-sync/frontend/js/components/github-sync-widget.tsx'
),

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

P2 Badge Gate GitHub UI imports on feature flag

These module imports are always enabled in overleafModuleImports, so GitHub UI surfaces even when GITHUB_SYNC_ENABLED is false. In that configuration the backend router is not mounted, so users still see GitHub actions but hit missing endpoints (for example status/auth routes), which breaks the intended disable switch for this feature.

Useful? React with 👍 / 👎.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant