Files
Verso/.gitea/workflows/deploy-verso.yml
T
claude 28a578ec85
Build and Deploy Verso / deploy (push) Successful in 7m46s
Fix untranslated UI keys (raw "snake_case" labels) + anonymous edit links
The frontend bundles only the locale keys listed in
frontend/extracted-translations.json (a custom webpack translations-loader
filters en.json to that set, normally regenerated by i18next-scanner). Every
key added by hand to en.json without also adding it here renders as its raw
key — which is why "blank_quarto_project", "share_compiled_presentation", etc.
showed up literally in the New-project menu and Share dialog.

Add all introduced keys to extracted-translations.json: blank_/example_
{quarto,latex,typst}_project, share_compiled_presentation(_info),
presentation_link_{members,private,public}, reset_link.

Also enable anonymous read-AND-write share links (edit without an account) via
OVERLEAF_ALLOW_ANONYMOUS_READ_AND_WRITE_SHARING; read-only links already worked
through OVERLEAF_ALLOW_PUBLIC_ACCESS.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-06-01 19:40:47 +00:00

326 lines
12 KiB
YAML

name: Build and Deploy Verso
on:
push:
branches:
- main
workflow_dispatch:
env:
SITE_URL: https://test.alocoq.fr
jobs:
deploy:
runs-on: native
timeout-minutes: 240
steps:
- name: Build and push Verso images with BuildKit
run: |
kubectl -n ci delete job verso-buildkit --ignore-not-found=true --wait=true
cat <<'EOF' | kubectl apply -f -
apiVersion: batch/v1
kind: Job
metadata:
name: verso-buildkit
namespace: ci
spec:
backoffLimit: 0
template:
spec:
restartPolicy: Never
initContainers:
- name: prepare
image: alpine/git:latest
command: ["sh", "-c"]
args:
- |
set -eux
REG=registry.git.svc.cluster.local:5000
git clone --depth 1 https://git.alocoq.fr/alois/verso.git /workspace/repo
# (#1) Build the base image only when it actually changes.
# The base layers' only repo input is Dockerfile-base, so
# we key on a content hash of that file: the base is tagged
# verso-base:base-<hash> and the app builds FROM that exact
# tag. If a base with this hash is already in the registry,
# the heavy base build (apt, TeX Live, Quarto) is skipped.
BTAG=$(sha256sum /workspace/repo/server-ce/Dockerfile-base | cut -c1-16)
printf '%s' "$BTAG" > /workspace/base_tag
if wget -qO- "http://$REG/v2/verso-base/tags/list" 2>/dev/null | grep -q "\"base-$BTAG\""; then
echo "Base image base-$BTAG already present - skipping base build"
else
touch /workspace/build-base
echo "Base image base-$BTAG not found - base will be built"
fi
volumeMounts:
- name: workspace
mountPath: /workspace
containers:
- name: buildkit
image: moby/buildkit:latest
securityContext:
privileged: true
command: ["sh", "-c"]
args:
- |
set -eux
# Push to the in-cluster registry (plain HTTP) to bypass
# the Traefik ingress, whose read timeout was killing the
# multi-GB TeX Live layer upload mid-stream. Mark the
# registry http+insecure so both push and the base pull
# for the app build treat it as plain HTTP. Written inside
# the container so no extra k8s resources are needed.
REG=registry.git.svc.cluster.local:5000
mkdir -p /etc/buildkit
printf '[registry."%s"]\n http = true\n insecure = true\n' "$REG" > /etc/buildkit/buildkitd.toml
BTAG=$(cat /workspace/base_tag)
BASE_REF="$REG/verso-base:base-$BTAG"
# (#1) Base build, only when prepare flagged it changed.
# (#2) Import/export a registry layer cache so that, when
# the base does change, unchanged layers (e.g. apt) are
# still reused instead of rebuilt from scratch.
if [ -f /workspace/build-base ]; then
buildctl-daemonless.sh build \
--frontend=dockerfile.v0 \
--local context=/workspace/repo \
--local dockerfile=/workspace/repo/server-ce \
--opt filename=Dockerfile-base \
--import-cache type=registry,ref=$REG/verso-cache:base \
--export-cache type=registry,ref=$REG/verso-cache:base,mode=max \
--output type=image,name=$BASE_REF,push=true,registry.insecure=true
else
echo "Reusing existing base image $BASE_REF"
fi
# App image, built FROM the content-pinned base tag.
# (#2) The registry cache lets yarn install be skipped when
# package.json is unchanged; the web build only re-runs
# when the frontend source actually changes.
buildctl-daemonless.sh build \
--frontend=dockerfile.v0 \
--local context=/workspace/repo \
--local dockerfile=/workspace/repo/server-ce \
--opt filename=Dockerfile \
--opt build-arg:OVERLEAF_BASE_TAG=$BASE_REF \
--import-cache type=registry,ref=$REG/verso-cache:app \
--export-cache type=registry,ref=$REG/verso-cache:app,mode=max \
--output type=image,name=$REG/verso:latest,push=true,registry.insecure=true
volumeMounts:
- name: workspace
mountPath: /workspace
volumes:
- name: workspace
emptyDir: {}
EOF
- name: Wait for build
run: |
kubectl -n ci wait --for=condition=complete job/verso-buildkit --timeout=14400s
- name: Show build logs
if: always()
run: |
kubectl -n ci logs job/verso-buildkit -c prepare || true
kubectl -n ci logs job/verso-buildkit -c buildkit || true
- name: Recreate test dependencies
run: |
kubectl -n test delete deployment mongo redis --ignore-not-found=true --wait=true
kubectl -n test delete service mongo redis --ignore-not-found=true --wait=true
cat <<'EOF' | kubectl apply -f -
apiVersion: apps/v1
kind: Deployment
metadata:
name: mongo
namespace: test
spec:
replicas: 1
selector:
matchLabels:
app: mongo
template:
metadata:
labels:
app: mongo
spec:
containers:
- name: mongo
image: mongo:8
command: ["mongod", "--replSet", "rs0", "--bind_ip_all"]
ports:
- containerPort: 27017
volumeMounts:
- name: mongo-data
mountPath: /data/db
volumes:
- name: mongo-data
emptyDir: {}
---
apiVersion: v1
kind: Service
metadata:
name: mongo
namespace: test
spec:
selector:
app: mongo
ports:
- name: mongo
port: 27017
targetPort: 27017
---
apiVersion: apps/v1
kind: Deployment
metadata:
name: redis
namespace: test
spec:
replicas: 1
selector:
matchLabels:
app: redis
template:
metadata:
labels:
app: redis
spec:
containers:
- name: redis
image: redis:7
ports:
- containerPort: 6379
volumeMounts:
- name: redis-data
mountPath: /data
volumes:
- name: redis-data
emptyDir: {}
---
apiVersion: v1
kind: Service
metadata:
name: redis
namespace: test
spec:
selector:
app: redis
ports:
- name: redis
port: 6379
targetPort: 6379
EOF
kubectl -n test rollout status deployment/mongo --timeout=180s
kubectl -n test rollout status deployment/redis --timeout=180s
sleep 5
kubectl -n test exec deploy/mongo -- mongosh --eval '
rs.initiate({
_id: "rs0",
members: [{ _id: 0, host: "mongo:27017" }]
})
'
kubectl -n test exec deploy/mongo -- mongosh --eval '
while (rs.status().myState !== 1) {
sleep(1000)
}
print("Mongo replica set is PRIMARY")
'
- name: Ensure Verso deployment exists
run: |
cat <<'EOF' | kubectl apply -f -
apiVersion: apps/v1
kind: Deployment
metadata:
name: verso
namespace: test
spec:
replicas: 1
selector:
matchLabels:
app: verso
template:
metadata:
labels:
app: verso
spec:
containers:
- name: verso
# Pull via the public address: the cluster nodes' containerd
# is configured for registry.alocoq.fr, not the in-cluster
# service name. Both front the same registry storage, so the
# image pushed via the in-cluster address resolves here too.
image: registry.alocoq.fr/verso:latest
ports:
- containerPort: 80
env:
- name: OVERLEAF_MONGO_URL
value: mongodb://mongo:27017/sharelatex?replicaSet=rs0
- name: OVERLEAF_REDIS_HOST
value: redis
- name: REDIS_HOST
value: redis
- name: OVERLEAF_APP_NAME
value: Verso
- name: OVERLEAF_SITE_URL
value: https://test.alocoq.fr
# Allow anonymous visitors to reach the site so link
# sharing and public presentation links work without a
# login. Per-project and per-route access checks still
# apply; private presentation links still require login.
- name: OVERLEAF_ALLOW_PUBLIC_ACCESS
value: "true"
# Also let anonymous visitors use read-AND-write share
# links (edit without an account). Read-only links only
# need OVERLEAF_ALLOW_PUBLIC_ACCESS above.
- name: OVERLEAF_ALLOW_ANONYMOUS_READ_AND_WRITE_SHARING
value: "true"
---
apiVersion: v1
kind: Service
metadata:
name: verso
namespace: test
spec:
selector:
app: verso
ports:
- name: http
port: 80
targetPort: 80
EOF
- name: Deploy Verso image
run: |
kubectl -n test set image deployment/verso \
verso=registry.alocoq.fr/verso:latest
kubectl -n test rollout restart deployment/verso
kubectl -n test rollout status deployment/verso --timeout=300s
- name: Create admin user
run: |
sleep 20
kubectl -n test exec deploy/verso -- bash -lc '
cd /overleaf/services/web
node modules/server-ce-scripts/scripts/create-user \
--admin \
--email=test@example.com || true
'
- name: Cleanup
if: always()
run: |
kubectl -n ci delete job verso-buildkit --ignore-not-found=true --wait=true