fix: persist GlitchTip GzipChunk patch and prune old releases in CI
All checks were successful
Release / Test backend (push) Successful in 45s
Release / Check ui (push) Successful in 50s
Release / Docker / caddy (push) Successful in 43s
Release / Docker / backend (push) Successful in 2m46s
Release / Docker / runner (push) Successful in 2m43s
Release / Upload source maps (push) Successful in 4m36s
Release / Docker / ui (push) Successful in 4m38s
Release / Gitea Release (push) Successful in 4m46s

- Add homelab/glitchtip/files_api.py with GzipChunk fallback for sentry-cli 3.x
  raw zip uploads; bind-mount into both glitchtip-web and glitchtip-worker
- Add release.yaml prune step to delete all but the 10 newest GlitchTip releases
- Reader page: remove dead code and simplify layout
This commit is contained in:
Admin
2026-04-05 17:30:39 +05:00
parent a3ad54db70
commit d0c95889ca
4 changed files with 200 additions and 110 deletions

View File

@@ -172,6 +172,8 @@ services:
MEDIA_ROOT: "/code/uploads"
volumes:
- glitchtip_uploads:/code/uploads
# Patch: GzipChunk fallback for sentry-cli 3.x raw zip uploads (GlitchTip bug)
- ./glitchtip/files_api.py:/code/apps/files/api.py:ro
healthcheck:
test: ["CMD", "python3", "-c", "import urllib.request; urllib.request.urlopen('http://localhost:8000/api/0/')"]
interval: 15s
@@ -196,6 +198,8 @@ services:
MEDIA_ROOT: "/code/uploads"
volumes:
- glitchtip_uploads:/code/uploads
# Patch: GzipChunk fallback for sentry-cli 3.x raw zip uploads (GlitchTip bug)
- ./glitchtip/files_api.py:/code/apps/files/api.py:ro
# ── Umami ───────────────────────────────────────────────────────────────────
umami:

View File

@@ -0,0 +1,127 @@
"""Port of sentry.api.endpoints.chunk.ChunkUploadEndpoint"""
import logging
from gzip import GzipFile
from io import BytesIO
from django.conf import settings
from django.shortcuts import aget_object_or_404
from django.urls import reverse
from ninja import File, Router
from ninja.errors import HttpError
from ninja.files import UploadedFile
from apps.organizations_ext.models import Organization
from glitchtip.api.authentication import AuthHttpRequest
from glitchtip.api.decorators import optional_slash
from glitchtip.api.permissions import has_permission
from .models import FileBlob
# Force just one blob
CHUNK_UPLOAD_BLOB_SIZE = 32 * 1024 * 1024 # 32MB
MAX_CHUNKS_PER_REQUEST = 1
MAX_REQUEST_SIZE = CHUNK_UPLOAD_BLOB_SIZE
MAX_CONCURRENCY = 1
HASH_ALGORITHM = "sha1"
CHUNK_UPLOAD_ACCEPT = (
"debug_files", # DIF assemble
"release_files", # Release files assemble
"pdbs", # PDB upload and debug id override
"sources", # Source artifact bundle upload
"artifact_bundles", # Artifact bundles contain debug ids to link source to sourcemaps
"proguard",
)
class GzipChunk(BytesIO):
def __init__(self, file):
raw = file.read()
try:
data = GzipFile(fileobj=BytesIO(raw), mode="rb").read()
except Exception:
# sentry-cli 3.x sends raw (uncompressed) zip data despite gzip being
# advertised by the server — fall back to using the raw bytes as-is.
data = raw
self.size = len(data)
self.name = file.name
super().__init__(data)
router = Router()
@optional_slash(router, "get", "organizations/{slug:organization_slug}/chunk-upload/")
async def get_chunk_upload_info(request: AuthHttpRequest, organization_slug: str):
"""Get server settings for chunk file upload"""
path = reverse("api:get_chunk_upload_info", args=[organization_slug])
url = (
path
if settings.GLITCHTIP_CHUNK_UPLOAD_USE_RELATIVE_URL
else settings.GLITCHTIP_URL.geturl() + path
)
return {
"url": url,
"chunkSize": CHUNK_UPLOAD_BLOB_SIZE,
"chunksPerRequest": MAX_CHUNKS_PER_REQUEST,
"maxFileSize": 2147483648,
"maxRequestSize": MAX_REQUEST_SIZE,
"concurrency": MAX_CONCURRENCY,
"hashAlgorithm": HASH_ALGORITHM,
"compression": ["gzip"],
"accept": CHUNK_UPLOAD_ACCEPT,
}
@optional_slash(router, "post", "organizations/{slug:organization_slug}/chunk-upload/")
@has_permission(["project:write", "project:admin", "project:releases"])
async def chunk_upload(
request: AuthHttpRequest,
organization_slug: str,
file_gzip: list[UploadedFile] = File(...),
):
"""Upload one more more gzipped files to save"""
logger = logging.getLogger("glitchtip.files")
logger.info("chunkupload.start")
organization = await aget_object_or_404(
Organization, slug=organization_slug.lower(), users=request.auth.user_id
)
files = [GzipChunk(chunk) for chunk in file_gzip]
if len(files) == 0:
# No files uploaded is ok
logger.info("chunkupload.end", extra={"status": 200})
return
logger.info("chunkupload.post.files", extra={"len": len(files)})
# Validate file size
checksums = []
size = 0
for chunk in files:
size += chunk.size
if chunk.size > CHUNK_UPLOAD_BLOB_SIZE:
logger.info("chunkupload.end", extra={"status": 400})
raise HttpError(400, "Chunk size too large")
checksums.append(chunk.name)
if size > MAX_REQUEST_SIZE:
logger.info("chunkupload.end", extra={"status": 400})
raise HttpError(400, "Request too large")
if len(files) > MAX_CHUNKS_PER_REQUEST:
logger.info("chunkupload.end", extra={"status": 400})
raise HttpError(400, "Too many chunks")
try:
await FileBlob.from_files(
zip(files, checksums), organization=organization, logger=logger
)
except IOError as err:
logger.info("chunkupload.end", extra={"status": 400})
raise HttpError(400, str(err)) from err
logger.info("chunkupload.end", extra={"status": 200})