Fix Quarto compile pipeline: install, logs, template, draft mode
Build and Deploy Verso / deploy (push) Successful in 10m58s
Build and Deploy Verso / deploy (push) Successful in 10m58s
Dockerfile-base: remove TeX Live (no longer needed), install Quarto 1.6.39 which bundles Typst for PDF output. This was the root cause of all compile failures — the server-ce monolith never had Quarto. QuartoRunner: run quarto via /bin/sh so stderr is merged into stdout with 2>&1; write combined output to output.log (not output.stdout) so the PDF-preview log panel picks it up and shows raw output. Also write the log on error so failures are always visible. CompileManager: guard DraftModeManager behind an isLatexFile check — injecting LaTeX preamble commands into a .qmd file corrupts it and causes a guaranteed compile failure when draft mode is requested. ProjectCreationHandler + mainbasic.qmd: new projects now create main.qmd with a minimal Quarto/Typst frontmatter instead of the LaTeX main.tex; _createRootDoc names the file main.qmd accordingly. Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
This commit is contained in:
@@ -4,10 +4,6 @@
|
||||
|
||||
FROM phusion/baseimage:noble-1.0.3
|
||||
|
||||
# Makes sure LuaTex cache is writable
|
||||
# -----------------------------------
|
||||
ENV TEXMFVAR=/var/lib/overleaf/tmp/texmf-var
|
||||
|
||||
# Update to ensure dependencies are updated
|
||||
# ------------------------------------------
|
||||
ENV REBUILT_AFTER="2026-05-21"
|
||||
@@ -39,45 +35,12 @@ RUN --mount=type=cache,target=/var/cache/apt,sharing=locked \
|
||||
/etc/nginx/nginx.conf \
|
||||
/etc/nginx/sites-enabled/default
|
||||
|
||||
# Install TexLive
|
||||
# ---------------
|
||||
# CTAN mirrors occasionally fail, in that case install TexLive using a
|
||||
# different server, for example https://ctan.crest.fr
|
||||
#
|
||||
# # docker build \
|
||||
# --build-arg TEXLIVE_MIRROR=https://ctan.crest.fr/tex-archive/systems/texlive/tlnet \
|
||||
# -f Dockerfile-base -t sharelatex/sharelatex-base .
|
||||
ARG TEXLIVE_MIRROR=https://mirror.ox.ac.uk/sites/ctan.org/systems/texlive/tlnet
|
||||
|
||||
RUN mkdir /install-tl-unx \
|
||||
&& wget --quiet https://tug.org/texlive/files/texlive.asc \
|
||||
&& gpg --import texlive.asc \
|
||||
&& rm texlive.asc \
|
||||
&& wget --quiet ${TEXLIVE_MIRROR}/install-tl-unx.tar.gz \
|
||||
&& wget --quiet ${TEXLIVE_MIRROR}/install-tl-unx.tar.gz.sha512 \
|
||||
&& wget --quiet ${TEXLIVE_MIRROR}/install-tl-unx.tar.gz.sha512.asc \
|
||||
&& gpg --verify install-tl-unx.tar.gz.sha512.asc \
|
||||
&& sha512sum -c install-tl-unx.tar.gz.sha512 \
|
||||
&& tar -xz -C /install-tl-unx --strip-components=1 -f install-tl-unx.tar.gz \
|
||||
&& rm install-tl-unx.tar.gz* \
|
||||
&& echo "tlpdbopt_autobackup 0" >> /install-tl-unx/texlive.profile \
|
||||
&& echo "tlpdbopt_install_docfiles 0" >> /install-tl-unx/texlive.profile \
|
||||
&& echo "tlpdbopt_install_srcfiles 0" >> /install-tl-unx/texlive.profile \
|
||||
&& echo "selected_scheme scheme-basic" >> /install-tl-unx/texlive.profile \
|
||||
\
|
||||
&& /install-tl-unx/install-tl \
|
||||
-profile /install-tl-unx/texlive.profile \
|
||||
-repository ${TEXLIVE_MIRROR} \
|
||||
\
|
||||
&& $(find /usr/local/texlive -name tlmgr) path add \
|
||||
&& tlmgr install --repository ${TEXLIVE_MIRROR} \
|
||||
latexmk \
|
||||
texcount \
|
||||
synctex \
|
||||
etoolbox \
|
||||
xetex \
|
||||
&& tlmgr path add \
|
||||
&& rm -rf /install-tl-unx
|
||||
# Install Quarto (bundles Typst for PDF rendering — no LaTeX needed)
|
||||
# ------------------------------------------------------------------
|
||||
ARG QUARTO_VERSION=1.6.39
|
||||
RUN curl -fsSL "https://github.com/quarto-dev/quarto-cli/releases/download/v${QUARTO_VERSION}/quarto-${QUARTO_VERSION}-linux-amd64.deb" -o /tmp/quarto.deb \
|
||||
&& dpkg -i /tmp/quarto.deb \
|
||||
&& rm /tmp/quarto.deb
|
||||
|
||||
|
||||
# Set up overleaf user and home directory
|
||||
|
||||
@@ -119,7 +119,11 @@ async function doCompile(request, stats, timings) {
|
||||
)
|
||||
|
||||
// apply a series of file modifications/creations for draft mode and tikz
|
||||
if (request.draft) {
|
||||
// Draft mode injects LaTeX preamble commands — skip for Quarto files
|
||||
const isLatexFile = /\.(tex|ltx|Rtex)$/i.test(
|
||||
request.rootResourcePath || ''
|
||||
)
|
||||
if (request.draft && isLatexFile) {
|
||||
await DraftModeManager.promises.injectDraftMode(
|
||||
Path.join(compileDir, request.rootResourcePath)
|
||||
)
|
||||
|
||||
@@ -8,14 +8,7 @@ import fs from 'node:fs'
|
||||
const ProcessTable = {}
|
||||
|
||||
function runQuarto(compileName, options, callback) {
|
||||
const {
|
||||
directory,
|
||||
mainFile,
|
||||
image,
|
||||
environment,
|
||||
compileGroup,
|
||||
timings,
|
||||
} = options
|
||||
const { directory, mainFile, image, environment, compileGroup } = options
|
||||
const timeout = options.timeout || 60000
|
||||
|
||||
logger.debug({ directory, timeout, mainFile, compileGroup }, 'starting quarto compile')
|
||||
@@ -33,46 +26,44 @@ function runQuarto(compileName, options, callback) {
|
||||
null,
|
||||
function (error, output) {
|
||||
delete ProcessTable[compileName]
|
||||
if (error) return callback(error)
|
||||
_writeLogOutput(compileName, directory, output, () => {
|
||||
callback(null, output)
|
||||
})
|
||||
if (error) {
|
||||
// Still try to write whatever output we captured before the error
|
||||
_writeLogOutput(compileName, directory, output, () => callback(error))
|
||||
return
|
||||
}
|
||||
_writeLogOutput(compileName, directory, output, () => callback(null, output))
|
||||
}
|
||||
)
|
||||
}
|
||||
|
||||
function _buildQuartoCommand(mainFile) {
|
||||
// Use Typst as the PDF engine — it is bundled with Quarto (>= 1.4) and
|
||||
// does not require a separate LaTeX installation.
|
||||
return [
|
||||
// Run through a POSIX shell so we can merge stderr into stdout.
|
||||
// Quarto writes all progress and error messages to stderr; without this
|
||||
// merge the UI would show nothing in the log panel on failure.
|
||||
// LocalCommandRunner replaces $COMPILE_DIR before the shell sees the string,
|
||||
// so the substitution is safe and no shell variable expansion occurs.
|
||||
const quartoArgs = [
|
||||
'quarto',
|
||||
'render',
|
||||
Path.join('$COMPILE_DIR', mainFile),
|
||||
'--to', 'typst',
|
||||
'--output', 'output.pdf',
|
||||
]
|
||||
`$COMPILE_DIR/${mainFile}`,
|
||||
'--to',
|
||||
'typst',
|
||||
'--output',
|
||||
'output.pdf',
|
||||
].join(' ')
|
||||
return ['/bin/sh', '-c', `${quartoArgs} 2>&1`]
|
||||
}
|
||||
|
||||
function _writeLogOutput(compileName, directory, output, callback) {
|
||||
if (!output) return callback()
|
||||
|
||||
function _writeFile(file, content, cb) {
|
||||
if (content && content.length > 0) {
|
||||
fs.unlink(file, () => {
|
||||
fs.writeFile(file, content, { flag: 'wx' }, err => {
|
||||
const content = (output && output.stdout) || ''
|
||||
if (!content) return callback()
|
||||
// Write to output.log so the PDF-preview log panel picks it up
|
||||
const logFile = Path.join(directory, 'output.log')
|
||||
fs.unlink(logFile, () => {
|
||||
fs.writeFile(logFile, content, { flag: 'wx' }, err => {
|
||||
if (err) {
|
||||
logger.error({ err, compileName, file }, 'error writing log file')
|
||||
logger.error({ err, compileName, logFile }, 'error writing quarto log')
|
||||
}
|
||||
cb()
|
||||
})
|
||||
})
|
||||
} else {
|
||||
cb()
|
||||
}
|
||||
}
|
||||
|
||||
_writeFile(Path.join(directory, 'output.stdout'), output.stdout, () => {
|
||||
_writeFile(Path.join(directory, 'output.stderr'), output.stderr, () => {
|
||||
callback()
|
||||
})
|
||||
})
|
||||
|
||||
@@ -88,7 +88,7 @@ async function createProjectFromSnippet(ownerId, projectName, docLines) {
|
||||
async function createBasicProject(ownerId, projectName) {
|
||||
const project = await _createBlankProject(ownerId, projectName)
|
||||
|
||||
const docLines = await _buildTemplate('mainbasic.tex', ownerId, projectName)
|
||||
const docLines = await _buildTemplate('mainbasic.qmd', ownerId, projectName)
|
||||
await _createRootDoc(project, ownerId, docLines)
|
||||
|
||||
AnalyticsManager.recordEventForUserInBackground(ownerId, 'project-created', {
|
||||
@@ -304,7 +304,7 @@ async function _createRootDoc(project, ownerId, docLines) {
|
||||
const { doc } = await ProjectEntityUpdateHandler.promises.addDoc(
|
||||
project._id,
|
||||
project.rootFolder[0]._id,
|
||||
'main.tex',
|
||||
'main.qmd',
|
||||
docLines,
|
||||
ownerId,
|
||||
null
|
||||
|
||||
@@ -0,0 +1,9 @@
|
||||
---
|
||||
title: "<%= project_name %>"
|
||||
author: "<%= user.first_name %> <%= user.last_name %>"
|
||||
date: "<%= month %> <%= year %>"
|
||||
format: typst
|
||||
---
|
||||
|
||||
## Introduction
|
||||
|
||||
Reference in New Issue
Block a user