Skip to content

[UPSTREAM CHANGES] latest changes as of Mon Jun 01 2026 01:19:22 GMT+0000 (Coordinated Universal Time) - #231

Open
github-actions[bot] wants to merge 5535 commits into
masterfrom
upstream-changes-2026-06-01
Open

[UPSTREAM CHANGES] latest changes as of Mon Jun 01 2026 01:19:22 GMT+0000 (Coordinated Universal Time)#231
github-actions[bot] wants to merge 5535 commits into
masterfrom
upstream-changes-2026-06-01

Conversation

@github-actions

@github-actions github-actions Bot commented Jun 1, 2026

Copy link
Copy Markdown

This PR is auto-generated by
actions/github-script.

bufke and others added 30 commits March 31, 2026 14:24
Subscription usage charts

See merge request glitchtip/glitchtip-backend!2218
Two concurrent workers upserting overlapping rows in logs_logresource
could lock tuples in different orders due to set iteration being
non-deterministic. Sorting ensures consistent lock acquisition order.

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
query_cold_parquet_files scanned all parquet files oldest-first,
even for limit=1 queries like /events/latest/. For orgs with many
date files this caused DuckDB OOM (244 MiB limit).

Reverse iteration order (newest-first) and stop scanning once
enough rows are collected, since UUIDv7 ordering guarantees older
files cannot outrank results already found in newer files.

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
The command was not populating issue_events_issueaggregate, so the
issues-stats API returned empty data for sample issues. Now generates
per-issue hourly aggregate counts alongside the existing project stats.

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
feat: populate IssueAggregate in make_sample_issues

See merge request glitchtip/glitchtip-backend!2260
Adds orphaned task rescue, max_rescues crash loop prevention,
and worker shutdown fixes for ASGI embedding.

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
chore(deps): upgrade django-vtasks 2.0.0 → 2.1.0

See merge request glitchtip/glitchtip-backend!2261
chore(deps): update dependency aiohttp to v3.13.5

See merge request glitchtip/glitchtip-backend!2262
chore(deps): update dependency boto3 to v1.42.80

See merge request glitchtip/glitchtip-backend!2263
chore(deps): update dependency orjson to v3.11.8

See merge request glitchtip/glitchtip-backend!2264
AuthenticationError("message") passes the message as the first positional
arg (status_code) to HttpError, causing Django to crash with
"HTTP status code must be an integer" when ninja's error handler passes
the string to HttpResponse(status=...).

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
chore(deps): update dependency boto3 to v1.42.81

See merge request glitchtip/glitchtip-backend!2267
chore(deps): update dependency django-stubs to v6.0.2

See merge request glitchtip/glitchtip-backend!2268
chore(deps): update dependency locust to v2.43.4

See merge request glitchtip/glitchtip-backend!2269
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
fix: use keyword arg for AuthenticationError to avoid string status_code

See merge request glitchtip/glitchtip-backend!2266
Update dependency boto3 to v1.42.82

See merge request glitchtip/glitchtip-backend!2270
Update dependency django-allauth to v65.15.1

See merge request glitchtip/glitchtip-backend!2271
Update dependency ruff to v0.15.9

See merge request glitchtip/glitchtip-backend!2272
bufke and others added 30 commits May 25, 2026 12:17
So contributors adding periodic tasks (cold-storage compaction, retention
sweeps, etc.) don't add an external lock to keep them from running on
every pod — django-vtasks already gives single-runner semantics for
scheduled entries. Manual ``aenqueue()`` calls still race and need their
own guard.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
…floor

Follow-up to the hour-tiered span storage commit. Three changes:

- Recover a missing hour-rollup on the next compaction tick by re-deriving
  it from the sealed raw file. Without recovery, a transient failure of
  ``_write_hour_rollup`` (slot saturation, transient S3) after a successful
  seal silently drops that hour from every trend query until raw retention
  expires. The seal itself is unchanged — recovery just fills in the gap
  the next time compaction visits the date.

- Floor the DuckDB concurrency cap at 2 (was ``max(1, DUCKDB_THREADS)``).
  On a 1-CPU pod the cap would otherwise be 1, and a single ``block=True``
  project-delete rewrite would freeze every dashboard read for the
  duration. Floor of 2 keeps reads moving in parallel with a rare blocking
  deletion, at the cost of 2 × memory_limit worst case.

- Rename ``table_name`` → ``storage_prefix`` at the cold-storage boundary.
  The value is a path component, not a PG identifier, and the old name was
  misleading at every call site that used it for path construction
  (``cleanup_all_cold_storage``, ``delete_org_cold_storage``,
  ``rewrite_parquet_excluding_project``, ``query_cold_parquet_files``,
  ``enumerate_*``). Partition-management functions in
  ``glitchtip/cold_storage.py`` that genuinely need a PG identifier keep
  ``table_name``; the rename stops at that boundary.

Tests cover rollup recovery (sealed-but-no-rollup -> next tick fills it
in) and legacy flat-chunk reads + day-roll (pre-MR ``org/{date}/chunk_*``
layout stays readable and gets folded into ``{date}.parquet`` on the
first daily roll, no standalone backfill needed). Full
``apps.performance`` + ``apps.organizations_ext`` + ``apps.projects`` +
``apps.logs`` + ``apps.issue_events`` suites pass.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
The process-wide ``duckdb_slot()`` semaphore is what keeps a burst of
concurrent reads from multiplying peak RSS, but it's acquired at the
call site rather than inside ``get_duckdb_read_connection`` — three
current callers do this correctly, but the contract was implicit.
Spell it out so a future caller doesn't silently break the memory
bound, and explain why the slot can't be folded into the getter
(inner helpers reuse the connection under an already-held slot).

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
perf(cold-storage): hour-tiered idempotent span storage + bounded reads

See merge request glitchtip/glitchtip-backend!2360
Add support for Feishu group bot webhooks as a new alert recipient type.
Feishu uses its own interactive card message format rather than the
Slack-compatible format, so a dedicated sender is required.

Also fix webhook tests that were failing because the SSRF guard
(_is_url_allowed) was blocking external URLs during DNS resolution in
the test environment. Mock the guard in setUp so all webhook tests run
correctly in isolation.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
feat: add Feishu (Lark) webhook alert recipient

See merge request glitchtip/glitchtip-backend!2365
django-async-backend is a recent addition and not yet released. Make it
optional so an installation can run on Django's stock sync ORM while
keeping all raw-SQL optimisations: the same async surface is shimmed
via ``sync_to_async`` when the flag is off.

The default is off. Setting ``USE_ASYNC_BACKEND=True`` restores native
async cursors, ``async_atomic`` and ``AsyncQuerySet``.

All conditional indirection lives in one module
(``glitchtip/async_compat.py``); the rest of the codebase imports the
async surface from there. To remove the layer later, delete that
module and switch ~6 import sites back to ``django_async_backend.X``.

Verified with the full test suites for ``apps.event_ingest``,
``apps.uptime``, ``apps.issue_events``, ``apps.performance``,
``apps.logs``, and ``glitchtip.tests`` under both modes, plus a live
ASGI sanity check (ORM-backed list endpoint + event ingest +
background task persistence) in both modes.

This change was developed with the assistance of AI; all code was
reviewed by a human before commit.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
refactor: make django-async-backend opt-in via USE_ASYNC_BACKEND

See merge request glitchtip/glitchtip-backend!2366
Replaces the per-org Organization.license_key approach (the previous
shape of this MR) with an instance-wide configuration model, per the
discussion on !2361.

A single-row SupportLicense model holds license_key + billing_email for
the whole instance. The classmethod resolver implements the precedence
chain:

1. BILLING_ENABLED (hosted/SaaS mode) → ("", "") — no support-license
   concept applies on hosted GlitchTip
2. Per-field env-wins — GLITCHTIP_LICENSE_KEY and the new
   GLITCHTIP_BILLING_EMAIL env vars each override the corresponding DB
   field if set; missing env values fall back to the DB row
3. Otherwise the singleton DB row

Env-wins matches every other env-controlled setting in the codebase
(no DB-overrides-env precedent anywhere). Per-field rather than
all-or-nothing so operators can mix-and-match without footguns.

Admin registration is superuser-only and only active when
BILLING_ENABLED is false (the support-license concept is meaningless
in SaaS mode). Singleton enforced via pk=1 in save() override.

Also extracts SUBSCRIPTION_ID_PATTERN to apps/stripe/validators.py so
the rewritten license-invoice endpoint can import the same regex.
… validator

- Extract _merge() helper for resolved/aresolved env-wins logic (was copy-pasted across sync/async pair)
- Drop IntegrityError retry in load/aload (defended against impossible race on admin-only-written singleton)
- Delete validators.py + SubscriptionIdPatternTestCase (dead since !2362 closed; key is only set via env/admin, not user input)
- Strip docstrings/comments that restate names or duplicate sibling-class patterns
…ion paths

License is now state stored in the DB singleton, not config. Operators set
their license via Django superuser admin only. Matches the licensing pattern
of GitLab EE, Mattermost, and Nexus self-hosted.

- Drop GLITCHTIP_LICENSE_KEY, GLITCHTIP_BILLING_EMAIL, I_PAID_FOR_GLITCHTIP env vars
- Simplify SupportLicense.resolved/aresolved to BILLING_ENABLED short-circuit + DB read
- Drop _merge() helper (no env to merge with)
- Trim resolver tests to 3 cases
…ense_key from public schema

Without this, the previous "admin-only" commit broke /api/settings/ in
isolation — it removed I_PAID_FOR_GLITCHTIP and GLITCHTIP_LICENSE_KEY
env vars but left get_settings still reading them, causing
AttributeError. Bundles the security fix (remove license_key from public
settings — David's first review) with the wiring fix so this MR is
self-consistent.

- get_settings derives iPaidForGlitchTip from SupportLicense.license_key (DB)
- BILLING_ENABLED short-circuits to True (hosted = always paid)
- Drop license_key field from SettingsOut (was leaking the operator's Stripe subscription identifier to anonymous callers)
- Hoist SupportLicense import in glitchtip/tests.py
- Add test_settings_does_not_expose_license_key
- Add test_i_paid_for_glitchtip_reflects_support_license
…r David)

Per David: "removing a deprecated feature is a BIG deal with asking
permission for, making it's own MR, targeting a MAJOR release." Restore
the legacy env var as a force-paid override so existing self-hosted
operators who set it (to silence the donate banner without a real license)
are not broken by upgrade. Deprecation to be handled in a separate MR
targeting a major release.

- Restore I_PAID_FOR_GLITCHTIP env in settings.py + BILLING_ENABLED branch
- get_settings: short-circuit to True when env is set, before SupportLicense lookup
- Add test_legacy_i_paid_env_var_still_overrides to document backward compat
…mments

Continuing David's "removing a deprecated feature is a BIG deal" rule:
both GLITCHTIP_LICENSE_KEY and I_PAID_FOR_GLITCHTIP env vars existed on
master before this branch. Restore them verbatim (declarations +
comments) so backward-compatibility is preserved for self-hosted
operators using either env on master.

- Restore GLITCHTIP_LICENSE_KEY env declaration and original comments
- Restore master's "Legacy setting" comment on I_PAID_FOR_GLITCHTIP (drop my prose)
- Restore env.bool default chain so GLITCHTIP_LICENSE_KEY env still feeds I_PAID_FOR_GLITCHTIP
- api.py "or settings.I_PAID_FOR_GLITCHTIP" fallback already covers both paths
- GLITCHTIP_BILLING_EMAIL stays removed (was never on master, added by this branch)
…G_ENABLED check

Comment clarifies the dual-source precedence (legacy env + new singleton)
and why the BILLING_ENABLED check is explicit rather than relying on the
env.bool chain (TESTING-mode settings quirk causes the chain to not propagate
to I_PAID_FOR_GLITCHTIP).
- api.py: trim 3-line iPaidForGlitchTip derivation comment to 1 line
- tests.py: drop 3 comments that restate the test name or the assertion
- test_support_license.py: drop comment that paraphrases the assertEqual above it
…er project convention

Per David: "No a-prefix allowed. If it's IO it's async." Drop the sync
load()/resolved() variants — they had no production callers, only
internal use by tests — and rename aload→load, aresolved→resolved on the
remaining async methods. Convert SupportLicense tests to async def to
match the existing apps/stripe/tests/ pattern.
…leak from public settings

Removes the unauthenticated exposure of license_key (which is a Stripe
subscription ID, today equivalent to a credential for the upcoming
license-invoice PII lookup). Replaces it with an authenticated
/api/0/instance-license/ endpoint that serves the resolved
SupportLicense to any logged-in user.

- Drop `license_key: str` field from SettingsOut + remove the
  corresponding entry from the get_settings handler. The unauthenticated
  /api/0/settings/ no longer returns license_key.
- New GET /api/0/instance-license/ returns
  {licenseKey, billingEmail, source} where source is "env"|"db"|"none".
  Reads from SupportLicense.aresolved() (env-wins precedence).
- Auth: any logged-in user (not superuser-restricted). Non-admins need
  the resolved signal to render the support-license-aware UI surfaces
  (banner, View Invoice, Support menu).

Builds on the SupportLicense singleton introduced in the rewritten
!2361. Lands alongside or before the license-invoice endpoint
(!2362 rewrite) to close the PII-scrape vector described in David's
review of !2362.
Per spec: license_key is a server-side credential and never flows to the
frontend. Future operator actions (support link, invoice link) will be
purpose-built endpoints that embed the key server-side.

- Drop license_key from InstanceLicenseOut schema and handler
- Replace test_response_omits_license_key + test_per_field_merge_env_key_with_db_email with focused tests covering env-source, DB-source, and BILLING_ENABLED short-circuit
Admin-set license now immediately enables paid features (e.g., Manage
Billing button) without requiring a restart, fulfilling the "admin or
env" promise that the env-only wiring previously broke.

- get_settings() reads SupportLicense.license_key from DB instead of settings.I_PAID_FOR_GLITCHTIP env
- BILLING_ENABLED short-circuits to True (hosted GlitchTip = always paid)
- Trim env-related InstanceLicenseTestCase decorators (env vars dropped in !2361)
- Add test_i_paid_for_glitchtip_reflects_support_license to verify wiring
Comment said /api/0/settings/, actual route is /api/settings/ (NinjaAPI
mounted at api/ with @api.get("settings/") — no 0/ prefix). David's
drive-by from !2364 review.
…d on !2361)

Follow-up to !2361's rename: get_instance_license now calls the renamed
async method without the a-prefix.
…1/foundation-and-sub-swap'

feat(api): authenticated instance-license endpoint; remove license_key from public settings

See merge request glitchtip/glitchtip-backend!2364
feat(stripe): instance-wide SupportLicense singleton + env-wins resolver + admin

See merge request glitchtip/glitchtip-backend!2361
Bumps symbolic from 12.18.x to 13.1.1, updating both pyproject.toml and
uv.lock together so `uv sync --frozen` (CI and the Docker build) stays
consistent.

The major version bump originates from symbolic's internal Rust
dependency updates (gimli, goblin, scroll) and added Windows ARM64
support; there are no breaking changes to the Python API GlitchTip uses
(SourceMapCache, normalize_debug_id, Archive, parse_addr, ProguardMapper,
SymCache). Full test suite passes locally (1071 tests).

Supersedes the Renovate MR, which only bumped pyproject.toml (leaving
uv.lock inconsistent) and had fallen behind master.

AI-assisted change (Claude Code); human review required before merge.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Lockfile-only bump from 2.4.0 to 2.5.0. The constraint in pyproject.toml
(~=2.2) already permits it. 2.5.0 newly declares django as an explicit
dependency (previously only prometheus-client), which is satisfied by the
existing top-level Django dependency.

Replaces the stale Renovate MR !2234, whose branch had become unmergeable
against master (uv.lock conflict). Note the branch-push pipeline on that MR
passed at the same SHA; only the merge-result pipeline failed, due to the
conflict — not a test regression.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
chore(deps): update symbolic to 13.1.1

See merge request glitchtip/glitchtip-backend!2369
chore(deps): update django-prometheus to 2.5.0

See merge request glitchtip/glitchtip-backend!2370
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.

9 participants