feat(git-sync): independent toggles for project files and PDF push
Build and Deploy Verso / deploy (push) Successful in 12m48s
Build and Deploy Verso / deploy (push) Successful in 12m48s
Two new boolean fields on the project (gitSyncPushFiles, gitSyncPushPdf, both default true) let users control what gets pushed independently: - "Push project files" switch — skip all docs/binary files when off - "Push compiled PDF" switch — grayed out when no pdfPath is set The push button and auto-push are disabled when both switches would result in nothing being pushed. Config is stored in MongoDB so settings persist per-project. Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
This commit is contained in:
co-authored by
Claude Sonnet 4.6
parent
9a474f7790
commit
be8aef44fe
@@ -5,7 +5,7 @@ import { expressify } from '@overleaf/promise-utils'
|
||||
|
||||
async function configureGitSync(req, res) {
|
||||
const projectId = req.params.project_id
|
||||
const { remoteUrl, subPath = '', pdfPath = '' } = req.body
|
||||
const { remoteUrl, subPath = '', pdfPath = '', pushFiles = true, pushPdf = true } = req.body
|
||||
|
||||
if (typeof remoteUrl !== 'string' || remoteUrl.trim() === '') {
|
||||
return res.status(400).json({ error: 'remoteUrl is required' })
|
||||
@@ -30,6 +30,8 @@ async function configureGitSync(req, res) {
|
||||
remoteUrl: trimmedUrl,
|
||||
subPath: trimmedSubPath,
|
||||
pdfPath: trimmedPdfPath,
|
||||
pushFiles: Boolean(pushFiles),
|
||||
pushPdf: Boolean(pushPdf),
|
||||
})
|
||||
logger.debug({ projectId }, 'git sync: config saved')
|
||||
res.sendStatus(204)
|
||||
@@ -40,7 +42,8 @@ async function pushToGit(req, res) {
|
||||
try {
|
||||
const userId = SessionManager.getLoggedInUserId(req.session)
|
||||
const { buildId, clsiServerId } = req.body ?? {}
|
||||
const { remoteUrl, subPath, pdfPath } = await GitSyncHandler.getConfig(projectId)
|
||||
const { remoteUrl, subPath, pdfPath, pushFiles, pushPdf } =
|
||||
await GitSyncHandler.getConfig(projectId)
|
||||
if (!remoteUrl) {
|
||||
return res.status(400).json({ error: 'No git remote configured for this project' })
|
||||
}
|
||||
@@ -50,6 +53,8 @@ async function pushToGit(req, res) {
|
||||
pdfBuildId: buildId,
|
||||
pdfClsiServerId: clsiServerId,
|
||||
userId,
|
||||
pushFiles,
|
||||
pushPdf,
|
||||
})
|
||||
res.sendStatus(204)
|
||||
} catch (err) {
|
||||
@@ -82,6 +87,8 @@ async function getGitSyncConfig(req, res) {
|
||||
remoteUrl: config.remoteUrl ?? '',
|
||||
subPath: config.subPath,
|
||||
pdfPath: config.pdfPath,
|
||||
pushFiles: config.pushFiles,
|
||||
pushPdf: config.pushPdf,
|
||||
})
|
||||
}
|
||||
|
||||
|
||||
@@ -59,10 +59,18 @@ async function spawnGit(args, cwd) {
|
||||
})
|
||||
}
|
||||
|
||||
async function setConfig(projectId, { remoteUrl, subPath, pdfPath }) {
|
||||
async function setConfig(projectId, { remoteUrl, subPath, pdfPath, pushFiles, pushPdf }) {
|
||||
await Project.updateOne(
|
||||
{ _id: new ObjectId(projectId) },
|
||||
{ $set: { gitRemote: remoteUrl, gitSyncPath: subPath, gitSyncPdfPath: pdfPath } }
|
||||
{
|
||||
$set: {
|
||||
gitRemote: remoteUrl,
|
||||
gitSyncPath: subPath,
|
||||
gitSyncPdfPath: pdfPath,
|
||||
gitSyncPushFiles: pushFiles,
|
||||
gitSyncPushPdf: pushPdf,
|
||||
},
|
||||
}
|
||||
)
|
||||
}
|
||||
|
||||
@@ -71,11 +79,15 @@ async function getConfig(projectId) {
|
||||
gitRemote: 1,
|
||||
gitSyncPath: 1,
|
||||
gitSyncPdfPath: 1,
|
||||
gitSyncPushFiles: 1,
|
||||
gitSyncPushPdf: 1,
|
||||
}).lean()
|
||||
return {
|
||||
remoteUrl: project?.gitRemote ?? null,
|
||||
subPath: project?.gitSyncPath ?? '',
|
||||
pdfPath: project?.gitSyncPdfPath ?? '',
|
||||
pushFiles: project?.gitSyncPushFiles ?? true,
|
||||
pushPdf: project?.gitSyncPushPdf ?? true,
|
||||
}
|
||||
}
|
||||
|
||||
@@ -83,39 +95,41 @@ async function pushToRemote(
|
||||
projectId,
|
||||
remoteUrl,
|
||||
subPath,
|
||||
{ pdfPath, pdfBuildId, pdfClsiServerId, userId } = {}
|
||||
{ pdfPath, pdfBuildId, pdfClsiServerId, userId, pushFiles = true, pushPdf = true } = {}
|
||||
) {
|
||||
const tmpDir = await mkdtemp(join(tmpdir(), 'verso-git-'))
|
||||
logger.debug({ projectId, tmpDir, subPath }, 'git sync: writing project to temp dir')
|
||||
logger.debug({ projectId, tmpDir, subPath, pushFiles, pushPdf }, 'git sync: writing project to temp dir')
|
||||
try {
|
||||
const fileRoot = subPath ? join(tmpDir, subPath) : tmpDir
|
||||
|
||||
// Fetch all text documents
|
||||
const docs = await ProjectEntityHandler.promises.getAllDocs(projectId)
|
||||
for (const [path, doc] of Object.entries(docs)) {
|
||||
const rel = path.startsWith('/') ? path.slice(1) : path
|
||||
const dest = join(fileRoot, rel)
|
||||
await mkdir(dirname(dest), { recursive: true })
|
||||
await writeFile(dest, doc.lines.join('\n'))
|
||||
}
|
||||
if (pushFiles) {
|
||||
// Fetch all text documents
|
||||
const docs = await ProjectEntityHandler.promises.getAllDocs(projectId)
|
||||
for (const [path, doc] of Object.entries(docs)) {
|
||||
const rel = path.startsWith('/') ? path.slice(1) : path
|
||||
const dest = join(fileRoot, rel)
|
||||
await mkdir(dirname(dest), { recursive: true })
|
||||
await writeFile(dest, doc.lines.join('\n'))
|
||||
}
|
||||
|
||||
// Fetch all binary files
|
||||
const files = await ProjectEntityHandler.promises.getAllFiles(projectId)
|
||||
for (const [path, file] of Object.entries(files)) {
|
||||
const rel = path.startsWith('/') ? path.slice(1) : path
|
||||
const dest = join(fileRoot, rel)
|
||||
await mkdir(dirname(dest), { recursive: true })
|
||||
const { stream } =
|
||||
await HistoryManager.promises.requestBlobWithProjectId(
|
||||
projectId,
|
||||
file.hash
|
||||
)
|
||||
await pipeline(stream, createWriteStream(dest))
|
||||
// Fetch all binary files
|
||||
const files = await ProjectEntityHandler.promises.getAllFiles(projectId)
|
||||
for (const [path, file] of Object.entries(files)) {
|
||||
const rel = path.startsWith('/') ? path.slice(1) : path
|
||||
const dest = join(fileRoot, rel)
|
||||
await mkdir(dirname(dest), { recursive: true })
|
||||
const { stream } =
|
||||
await HistoryManager.promises.requestBlobWithProjectId(
|
||||
projectId,
|
||||
file.hash
|
||||
)
|
||||
await pipeline(stream, createWriteStream(dest))
|
||||
}
|
||||
}
|
||||
|
||||
// Optionally include the compiled PDF at a configured path
|
||||
// clsiServerId is optional (absent in single-server CE deployments)
|
||||
if (pdfPath && pdfBuildId) {
|
||||
if (pushPdf && pdfPath && pdfBuildId) {
|
||||
const pdfStream = await ClsiManager.promises.getOutputFileStream(
|
||||
projectId,
|
||||
userId,
|
||||
|
||||
@@ -607,6 +607,8 @@ const _ProjectController = {
|
||||
gitRemote: 1,
|
||||
gitSyncPath: 1,
|
||||
gitSyncPdfPath: 1,
|
||||
gitSyncPushFiles: 1,
|
||||
gitSyncPushPdf: 1,
|
||||
}),
|
||||
userIsMemberOfGroupSubscription: sessionUser
|
||||
? (async () =>
|
||||
@@ -1029,6 +1031,8 @@ const _ProjectController = {
|
||||
gitRemote: project.gitRemote ?? '',
|
||||
gitSyncPath: project.gitSyncPath ?? '',
|
||||
gitSyncPdfPath: project.gitSyncPdfPath ?? '',
|
||||
gitSyncPushFiles: project.gitSyncPushFiles ?? true,
|
||||
gitSyncPushPdf: project.gitSyncPushPdf ?? true,
|
||||
wsUrl,
|
||||
showSupport: Features.hasFeature('support'),
|
||||
showTemplatesServerPro,
|
||||
|
||||
@@ -111,6 +111,8 @@ export const ProjectSchema = new Schema(
|
||||
gitRemote: { type: String },
|
||||
gitSyncPath: { type: String },
|
||||
gitSyncPdfPath: { type: String },
|
||||
gitSyncPushFiles: { type: Boolean },
|
||||
gitSyncPushPdf: { type: Boolean },
|
||||
},
|
||||
{ minimize: false }
|
||||
)
|
||||
|
||||
@@ -18,6 +18,8 @@ meta(name="ol-gitSyncEnabled" data-type="boolean" content=gitSyncEnabled)
|
||||
meta(name="ol-gitRemote" content=gitRemote)
|
||||
meta(name="ol-gitSyncPath" content=gitSyncPath)
|
||||
meta(name="ol-gitSyncPdfPath" content=gitSyncPdfPath)
|
||||
meta(name="ol-gitSyncPushFiles" data-type="boolean" content=gitSyncPushFiles)
|
||||
meta(name="ol-gitSyncPushPdf" data-type="boolean" content=gitSyncPushPdf)
|
||||
meta(name="ol-compilesUserContentDomain" content=settings.compilesUserContentDomain)
|
||||
//- enable doc hash checking for all projects
|
||||
//- used in public/js/libs/sharejs.js
|
||||
|
||||
Reference in New Issue
Block a user