[UPSTREAM CHANGES] latest changes as of Mon Jun 15 2026 01:23:01 GMT+0000 (Coordinated Universal Time) - #233
Open
github-actions[bot] wants to merge 5598 commits into
Open
[UPSTREAM CHANGES] latest changes as of
Mon Jun 15 2026 01:23:01 GMT+0000 (Coordinated Universal Time)#233github-actions[bot] wants to merge 5598 commits into
github-actions[bot] wants to merge 5598 commits into
Conversation
Update dependency boto3 to v1.42.86 See merge request glitchtip/glitchtip-backend!2287
Update dependency memray to v1.19.3 See merge request glitchtip/glitchtip-backend!2288
Update dependency symbolic to v12.17.4 See merge request glitchtip/glitchtip-backend!2289
feat: return multiple prices and marketing features from Stripe products API See merge request glitchtip/glitchtip-backend!2290
Update dependency boto3 to v1.42.87 See merge request glitchtip/glitchtip-backend!2291
Update dependency ruff to v0.15.10 See merge request glitchtip/glitchtip-backend!2292
Update dependency boto3 to v1.42.88 See merge request glitchtip/glitchtip-backend!2294
The heartbeat existence check in fetch() filtered uptime_monitorcheck only by monitor_id and start_check. Because the table is nested-partitioned RANGE (UUIDv7 id) -> HASH (organization_id), this forced Postgres to lock every range partition and every hash sub-partition on every heartbeat check, exhausting max_locks_per_transaction under concurrency and raising "out of shared memory". Add organization_id (hash key) and an id__gte bound derived from the interval window (range key) so the planner can prune to the relevant partitions. AI-assisted: diagnosis and fix drafted with Claude Code. Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
transform_parameterized_message crashed the entire ingest batch with TypeError when a client shipped a log entry whose %d/%f format specifiers were paired with string params (e.g. '%d' with '0'). The schema-level validator catches this for the positional case but leaves formatted empty, after which utils re-ran the % operator without protection. Wrap both the % and .format() calls in a best-effort try/except and fall back to the raw template on failure, so one bad payload no longer takes down a batch of events. AI-assisted: diagnosis and fix drafted with Claude Code. Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
fix: prune partitions in uptime heartbeat existence check See merge request glitchtip/glitchtip-backend!2295
fix: tolerate malformed parameterized log messages in ingest See merge request glitchtip/glitchtip-backend!2296
fix: accept ISO-8601 timestamps in log envelope items See merge request glitchtip/glitchtip-backend!2279
promote_spans was declared async def and then called the sync _promote via asyncio.to_thread(). That bypasses django-vtasks' sync-task wrapper (_run_sync_with_db_cleanup in django_vtasks/worker.py), which calls close_old_connections() before and after execution. The thread-local Django connection therefore survived across scheduled task runs, and eventually hit "OperationalError: the connection is closed" when PgBouncer reaped the idle backend between invocations (INTERNAL-6P, 73 events in 10 days). Flatten both wrappers to plain sync def. django-vtasks already dispatches sync tasks via asyncio.to_thread internally, so behavior is identical minus the stale-connection bug. The re-enqueue on truncation also becomes the sync .enqueue() variant. Add regression tests asserting both task functions stay sync, since reintroducing async def would silently re-break the cleanup path. AI-assisted: diagnosis and fix drafted with Claude Code. Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
Make the regression rationale self-contained for open source readers. Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
The prior fix flattened both task wrappers to sync def to route them through django-vtasks' _run_sync_with_db_cleanup path. That shipped safely but is inconsistent with the async-first codebase and blocks further async work here (e.g. native aenqueue). Flip the refactor: keep the task wrappers async, use async ORM (async for, .adelete()) for all Django DB work, and wrap only the genuinely sync bits (arro3 Parquet writes, Django storage, DuckDB) in sync_to_async with default thread_sensitive=True. Those hops route to Django's shared sync executor thread — the same thread django-vtasks cleans via _async_close_old_connections — so the original "connection is closed" failure mode is structurally impossible. Changes: - promotion.py::promote_spans is now async def. ORM calls use async for / .adelete(). _write_chunk_parquet and storage.delete are wrapped in sync_to_async at call site. Drops the raw connection.cursor() DELETE in favor of ORM .adelete() — same generated SQL, partition pruning preserved. - tasks.py task wrappers are async again. Imports hoisted with underscore prefixes to avoid the name shadowing footgun. compact_span_chunks (100% storage/DuckDB, no ORM) is wrapped in one top-level sync_to_async hop rather than ping-ponging per call. - test_cold_storage.py: task-invoking tests in PromoteSpansTestCase converted to async def + await with abulk_create/acount, baker.make wrapped where called from async context. CompactSpansTestCase tests stay sync (zero ORM). The sync-task regression class added in the prior approach is removed — it asserted the wrong invariant for the async-first target shape. Verified: - 89 apps/performance tests pass. - iscoroutinefunction(promote_spans.func) and (compact_span_chunks.func) both True. - ruff check + format clean. AI-assisted: planned and implemented with Claude Code. Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
The .adelete() path inlines every UUID as a SQL literal (21KB of query text for 500 UUIDs; proportionally worse at batch limit 100k) and wraps the statement in BEGIN/COMMIT. Raw id = ANY(%s) passes the UUID list as a single bound array parameter and skips the transaction wrap, which is what the original code was deliberately doing. Extract the cursor.execute call into a named sync helper and call it via sync_to_async so the surrounding task stays async. Doc comment on the helper explains why the ORM path is wrong to keep anyone from "fixing" it back. Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
Push sync_to_async down to the leaf calls (arro3 write, storage save/delete, os.makedirs) instead of wrapping the whole function at the call site. Keeps the async-first rule consistent: sync_to_async is a bandaid applied at the leaves of the call tree, not at interior nodes. Caller in promote_spans becomes a plain await. Test call sites use async_to_sync since TestCase.setUp is sync. Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
refactor: promote_spans / compact_span_chunks async-native See merge request glitchtip/glitchtip-backend!2297
Update dependency boto3 to v1.42.89 See merge request glitchtip/glitchtip-backend!2298
Update dependency duckdb to v1.5.2 See merge request glitchtip/glitchtip-backend!2299
fix(performance): derive SpanStaging id from server time See merge request glitchtip/glitchtip-backend!2380
Accept OpenTelemetry logs from any OTLP exporter (language SDKs, the Collector, auto-instrumentation) at the OTLP-standard /v1/logs path, with no sentry SDK involved. Both encodings the OTLP/HTTP spec defines are supported, multiplexed on Content-Type: application/x-protobuf (the default for OTel SDKs and the Collector) and application/json. The project is resolved from the DSN public key carried in an auth header -- Authorization: Bearer <key>, X-Sentry-Auth, or a sentry_key query param -- because native OTLP exporters point at a base endpoint and append /v1/logs, leaving no project id in the path. public_key is globally unique, so the key alone identifies the project. Records are normalized to the existing OTel log-record shape and reuse otel_log_to_log_item and the ingest_logs pipeline; resource attributes (service.name, deployment.environment.name, host.name) are hoisted onto each record. The /v1/ paths are added to the lean ingest middleware fast-path. Gated by the existing GLITCHTIP_ENABLE_LOGS flag. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
- Map an unset protobuf LogRecord body to "" instead of the literal string "None" (str(None) downstream); aligns protobuf with the JSON path. - Treat all-zero trace_id/span_id bytes as absent rather than storing a zero-UUID / span_id 0 (8 zero bytes is truthy, so the prior guard let it through). - Honor partial throttle rates (1-99%) on the OTLP path, matching the envelope hot path, so a throttled org/project isn't fully exempt. - Tolerate an optional trailing slash on /v1/logs in both the route and the ingest fast-path regex, avoiding a confusing 301-drops-body on misconfig. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
- Fold `Authorization: Bearer <key>` into the shared auth_from_request so the envelope and OTLP paths use one key extractor; delete the duplicate otlp_key_from_request. Bearer is harmless on the envelope path (SDKs don't send it). get_project_by_key now reuses auth_from_request. - Hoist the inline `from apps.projects.models import ProjectKey` (no circular import) and the opentelemetry-proto / protobuf imports to module top. - Tests: cover OTLP auth via X-Sentry-Auth and ?sentry_key (now shared), and drain the enqueued task in the trailing-slash test so it can't bleed into a later test's flush. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
…tamps Two review fixes for the native OTLP/HTTP logs endpoint. Cheap rejection: `get_project_by_key` resolved the project with a DB lookup on every request, with no equivalent of the block cache the envelope hot path keeps. A flood of unpaid traffic — an invalid DSN key, or a real key whose org is over quota — therefore cost an indexed Postgres lookup per request (and the lookup precedes the body read, but nothing stopped the repeat). Add the same block cache, keyed on the DSN public key since there is no project id on this path: a known-bad key caches "v", an over-quota or throttled org caches its throttle, and repeat requests are bounced from Valkey in one round trip before any DB hit or body read. Also fire the periodic `check_organization_throttle` out of band like the envelope path so an over-quota OTLP-only org gets throttled promptly rather than waiting on the 4-hour sweep. Observed-time fallback: `otel_log_to_log_item` read only time_unix_nano, so a record carrying only observed time (valid per the OTel log data model when the original timestamp is unknown) collapsed to the 1970 epoch on the JSON transport. Fall back to observed_time_unix_nano, matching what the protobuf decoder already does, so both transports agree. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Reverses the precedence from the prior commit. GLITCHTIP_LICENSE_KEY is the recommended self-hosted setup and represents the deployment's declared config, so it should win over a hand-edited admin DB row. Checking it first also lets resolved() return without a DB query on the common path. This restores the original env-wins design (e2617cd) that a later admin-only refactor had dropped.
fix(stripe): fall back to GLITCHTIP_LICENSE_KEY env var in resolved() See merge request glitchtip/glitchtip-backend!2389
fix(billing): report uptime/log usage consistently across periods See merge request glitchtip/glitchtip-backend!2387
The Monitor.interval field was a PositiveSmallIntegerField (Postgres smallint, max 32767) while its own MaxValueValidator allows up to 86400 (one day). The uptime monitor API also did not bound interval at all. As a result, creating or updating a monitor with an interval between 32768 and 86400 (e.g. 86400 for a daily check) passed validation and then crashed on save with `DataError: smallint out of range`, returning an HTTP 500 instead of accepting the value or returning a clean error. The validator's 86400 ceiling shows the intent is to allow up to one day, so widen the column to PositiveIntegerField rather than lowering the limit. Also add an explicit `interval: Annotated[int, Ge(1), Le(86400)]` bound to the MonitorIn schema so an out-of-range value is rejected at the API layer (clean 4xx) instead of reaching the database. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
difs_create_file_from_chunks iterated FileBlob.objects.filter( checksum__in=chunks) in arbitrary DB order and stored only blobs[0] as the file's content. A debug file uploaded as more than one chunk would therefore fail the checksum verification or, worse, be stored as just its first chunk. Order the blobs by the client-supplied chunk list, concatenate them in order while verifying the whole-file checksum, and persist the result as a single combined FileBlob (GlitchTip's File model points at one blob). Single-chunk uploads reuse the uploaded blob directly, so the common case stores no duplicate data and is unchanged. Also simplify difs_get_file_from_chunks: File.checksum is the whole-file SHA1, so a matching checksum already identifies identical content regardless of how it was chunked. Tests: assert the assembled blob content matches the whole file, that reassembly follows the request's chunk order (not DB order), single- chunk reuse, and missing-chunk handling. Verified end-to-end against both glitchtip-cli and the open-source sentry-cli uploading a 1.5MB debug file split into 6 chunks. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
… reader Add tests for a file whose chunks reference the same blob twice (content must be duplicated per chunk-list entry) and for assembling the same multi-chunk file twice (no second combined blob). Replace the iter(lambda ...) blob reader with a walrus while-loop to match files/models.py. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
…e trace_metric The sentry-compatible envelope endpoint validates each item's header against a `type` that must be one of three buckets: - supported: types we process (transaction, event, user_report, feedback, log, otel_log) - ignored: known types we deliberately accept-and-drop silently (session, client_report, attachment, replay_*, span, profile_chunk, ...) - unknown: anything in neither list, which raises a pydantic ValidationError and is reported for visibility in case the SDK spec evolved. Previously every unknown type produced a structurally identical error, so an error tracker folded ALL unknown types into one issue — masking the arrival of any genuinely new item type. Now an unknown-type failure (a literal_error on the `type` field) is captured under a per-type fingerprint `["envelope-unsupported-item-type", <type>]`, so each distinct new type surfaces as its own issue. Other header schema failures (e.g. a malformed `length`) keep the generic grouping so they aren't merged under a type. Also adds `trace_metric` to the ignored list. It is a metrics item emitted by newer sentry JS SDKs that GlitchTip does not support (too heavy/high-volume for its lightweight goal), so it is now accepted-and-dropped silently instead of perpetually flagging as unknown. AI-assisted change; human review required before merge. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
feat(ingest): fingerprint unknown envelope item types per type; ignore trace_metric See merge request glitchtip/glitchtip-backend!2393
Reassemble multi-chunk debug files in order See merge request glitchtip/glitchtip-backend!2392
fix(uptime): widen monitor interval column to fit its 86400 max See merge request glitchtip/glitchtip-backend!2391
feat(ingest): native OTLP/HTTP logs endpoint (POST /v1/logs) See merge request glitchtip/glitchtip-backend!2374
…dleware Decompression of ingest request bodies (gzip/deflate/br/zstd) moves from the hand-rolled Python DecompressBodyMiddleware into the gt_rust extension. There is no longer a toggle or a Python fallback: gt_rust is now a hard dependency of the ingest path. - gt_rust gains a generic decompress(body, encoding, max) primitive (glitchtip-rust 0.2.0). The envelope hot path uses parse_envelope to decompress + frame in a single Rust pass. - /envelope/ and /minidump/ (plain Django views) call gt_rust directly at the body-read seam. /store/ and /security/ (django-ninja) decompress in the ORJSON parser before schema validation, so their routing and status codes are unchanged. - glitchtip/middleware.py (the streaming Python decoders) is deleted and the middleware is removed from MIDDLEWARE and the ingest ASGI chain. - Brotli (a Python dependency only the middleware used) is dropped; zstandard stays for the test suite, which still compresses fixtures to exercise ingest. The win is bounded memory per request: gt_rust decompresses with the GIL released and a hard size cap (GLITCHTIP_MAX_UNZIPPED_PAYLOAD_SIZE), instead of materializing the decompressed body on the Python heap before the view runs. Requires glitchtip-rust>=0.2.0. Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Now that glitchtip-rust 0.2.0 (with the generic decompress() primitive) is published, resolve it from the registry to unblock CI. Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Drop the migration framing ("old middleware", "no longer", "prior loop")
from the Rust-decompression code and comments so they read correctly for
someone arriving fresh, explaining why the work is in Rust (bounded memory,
DOS safety) as steady-state fact rather than as a change from a prior state.
Document the Rust-decompression seam as a gotcha in AGENTS.md and extend the
clean-room terminology guidance (lowercase sentry for SDKs/wire formats;
capital-S Sentry only when distinguishing GlitchTip from the company).
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
0.2.1 bounds the attacker-declared envelope item length that could abort the worker process; pin to it so the vulnerable 0.2.0 can't satisfy the ingest dependency. Re-locked from the PyPI registry. Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
The native OTLP/HTTP logs endpoint (POST /v1/logs) landed on master after this branch was cut. It is a plain Django view served by the minimal ingest ASGI chain, which previously included DecompressBodyMiddleware — so a gzipped OTLP body was decompressed upstream before the view read it. This branch deletes that middleware, so the OTLP view now has to decompress at its own body-read seam, exactly like the /envelope/ and /minidump/ plain views. gzip on the wire is not exotic here: the OTLP/HTTP spec requires servers to support gzip, and the OpenTelemetry Collector's OTLP/HTTP exporter gzips by default — so real exporter traffic arrives Content-Encoding: gzip. Decompress via gt_rust (request_content_encoding + decompress_body) before decode_otlp_logs, mapping the oversized-payload cap to 413 and a garbled stream to 400. Adds an endpoint test that posts a real gzip-compressed protobuf batch. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
…ch_vector) Moves issue full-text search off the Issue table into a dedicated, hash-partitioned issue_events_issuesearchindex table and removes the old Issue.search_vector column + its GIN index entirely. The Issue.search_vector GIN was maintained on nearly every event but queried rarely. Worse, an issue's search document saturates fast (title, transaction, URL, stack filenames repeat across an issue's events), so the per-event append_and_limit_tsvector + GIN churn was largely redundant work on the hot ingest path, and it kept the Issue row wide and un-cacheable for the common issue-list reads. This is a hard cutover (no dual-write, no read-time OR fallback): few users search, and active issues repopulate the new index on their next event, so a brief post-deploy gap self-heals. A bounded backfill covers recently-active issues so search works immediately after deploy. - New IssueSearchIndex model: composite PK (issue_id, organization_id), hash-partitioned by organization_id, GIN on fts_document. No DB-level FKs (a partitioned table can't be an FK target), matching the existing partitioned-table convention. - Ingest writes ONLY the new index: a new issue inserts its row in _create_issue_and_hash; subsequent events upsert via a single unnest CTE (UPDATE existing rows, INSERT the rest). Raw search text is passed to append_and_limit_tsvector so the new table gets the same lexeme/size limiting, and re-tokenizing an already-serialized tsvector is avoided. The legacy Issue.search_vector UPDATE/INSERT is gone. - Search reads ONLY the index, scoped by organization_id so Postgres prunes hash partitions. list_issues / list_project_issues resolve organization_id on the text-search path so the pruning actually triggers (without the old column there is no fallback for a full-partition scan). - delete_issues_in_batches deletes IssueSearchIndex rows (no DB cascade on a no-FK partitioned table); regression-tested. - Migrations: 0019 creates the partitioned table; 0020 backfills recently active (GLITCHTIP_EVENT_HOT_DAYS) issues, keyset-paginated over the last_seen index (atomic=False, ON CONFLICT DO NOTHING) so cost scales with hot-issue count, not table size, and a timed-out deploy resumes on re-run; 0021 drops the search_vector GIN CONCURRENTLY (atomic=False, never blocks ingest); 0022 drops the column under a bounded lock_timeout (metadata-only, O(1)). Drops run after the backfill. - make_sample_issues populates IssueSearchIndex so seeded issues stay searchable. Issue is a non-partitioned table (too large to hash-partition today); dropping search_vector shrinks it, easing both issue-list cache pressure now and a future partitioning effort. DEPLOY ORDER: 0022 drops a column the old release reads/writes, so drain the old release before applying these migrations. AI-assisted (Claude Code); human review required before merge. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
…ation Combines the search-index teardown from four migrations to three. The old GIN drop (RemoveIndexConcurrently) and the backfill both require atomic=False / autocommit and are both idempotent (DROP INDEX CONCURRENTLY IF EXISTS; INSERT ... ON CONFLICT DO NOTHING), so they share one re-runnable migration: 0019 create table + partitions + GIN (atomic, expand) 0020 backfill hot issues + drop old GIN (atomic=False, both idempotent) 0021 drop Issue.search_vector column (atomic, contract) The destructive column drop stays in its own atomic migration so it keeps the transactional SET LOCAL lock_timeout guard and remains an isolated, deferrable contract step. Verified: applies clean on an empty DB, makemigrations --check reports no drift, and a 200k-issue/140k-hot seed produces the same end state as the four-migration version. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Move the per-event-updated columns (count, last_seen, last_release) plus status/level and the full-text vector off the wide Issue row onto a new hash-partitioned-by-organization IssueIndex table. Issue becomes small and near-static (no per-event rewrite), and these columns gain org-partition pruning on the issue list and search paths. - IssueIndex model (table issue_events_issueindex), one-to-one with Issue via the `index` relation. Issue exposes count/last_seen/status/level/last_release through read-only proxy properties, so the read API is unchanged; queries and sorts resolve through index__* and a post_save signal gives ORM-created Issues their leaf row (ingest writes the leaf via raw SQL). - Ingest writes a single leaf upsert per event; the Issue table is no longer touched on ingest. - Migrations: 0019 creates the leaf (alignment-optimal columns, DB defaults for rolling-deploy safety, org-scoped list indexes); 0020 two-phase backfill (mandatory fixed-width columns for all issues, then abandonable fts for hot issues, time-budget guarded); 0021 drops Issue.search_vector; 0022 drops the five hot columns from Issue. 0022 is deploy-order sensitive -- drain the old release before applying it. - Org-scoped reads constrain the leaf partition key so Postgres prunes the hash partitions (verified: 4 partitions scanned -> 1). All leaf status/count writes carry organization_id to prune likewise. Prepared with AI assistance (Claude Code). Human review required. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Runtime zstd decompression is entirely in gt_rust, so the only Python-side consumer of a zstd library was test_compression's fixture builder, which *produces* a zstd-encoded body to POST. Stdlib zstd (PEP 784) covers that on Python 3.14+, so the third-party zstandard dependency only existed to build the fixture on the 3.12 CI job. Drop the dependency outright (nothing else pulls it transitively; uv.lock loses it on every platform) and skip test_zstd_compression when stdlib compression.zstd is unavailable. The zstd decode path is the same gt_rust wheel on both CI jobs, so the 3.12 skip loses no real coverage — the path is still exercised on 3.14. When requires-python rises to >=3.14 the skip becomes dead and can go too. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
feat(ingest): move all request-body decompression into Rust; drop middleware See merge request glitchtip/glitchtip-backend!2388
feat: hot-split Issue into a partitioned IssueIndex (move hot columns off Issue) See merge request glitchtip/glitchtip-backend!2191
The issue list sorts by count/last_seen, which the feat/issue-search-index hot-split moved onto the one-to-one IssueIndex leaf (the ORM order keys are now index__count / index__last_seen). AsyncLinkHeaderPagination inherited the upstream _get_position_from_instance, which reads the ordering value with a flat getattr(instance, "index__last_seen") to build the next-page cursor. That raises AttributeError, so any issue list spanning more than one page 500s (the org issues endpoint, breaking the frontend list + its e2e specs). Override _get_position_from_instance to walk the __ relation path. The leaf is already select_related, so this never issues an extra query, and flat fields (first_seen, priority annotation, dict rows) are unaffected. Add a regression test that paginates issues sorted by the joined index fields; it reproduces the AttributeError without the fix. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
fix(api): resolve cursor position through relation paths in pagination See merge request glitchtip/glitchtip-backend!2395
… into upstream-changes-2026-06-15
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
This PR is auto-generated by
actions/github-script.