Repository navigation
Merging 0.5: introducing major bug fixes, security and stability improvements - #28
Merged
Merged
Conversation
Starlette 1.x removed the TemplateResponse(name, {"request": ...}) form.
Because requirements are unpinned, fresh image builds pull Starlette 1.x
and every page render fails with "TypeError: unhashable type: 'dict'".
The request-first signature works on all Starlette versions >= 0.29.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01MjPx3yn1u3MzoCV7DMYKwB
- CORS allowed every origin with credentials (ENV defaults to "dev"), so any website a user visited could call the API, including /settings/save. CORS is now off unless ALLOWED_ORIGINS lists explicit origins. - New SecurityMiddleware rejects state-changing requests whose Origin/Referer host doesn't match the app (Host, X-Forwarded-Host, app_public_url or CSRF_TRUSTED_ORIGINS). Clients that send neither header (curl, n8n) are unaffected. - Security headers: nosniff, Referrer-Policy, frame protection (configurable via FRAME_ANCESTORS for dashboard embedding) and a strict CSP. - Optional ALLOWED_HOSTS enables TrustedHostMiddleware (DNS rebinding). - Stored XSS: file names (including names of entries inside uploaded ZIPs) and tool error output were inserted into innerHTML unescaped. All dynamic values in the job table now go through escapeHtml(), and the duplicated details markup is built in one place. - choices.js 11.2.4 and Font Awesome 4.7.0 are vendored instead of loaded unpinned from CDNs, and the inline APP_CONFIG script became a JSON data block, so the CSP can restrict scripts to 'self' (and the UI works offline). - Locales that Intl rejects (e.g. "en-US@posix") no longer break the history. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01MjPx3yn1u3MzoCV7DMYKwB
- GET /settings had no auth check: in OIDC mode anyone could load it and read the OIDC client secret and webhook bearer token from the form. The page now requires login, shows the configuration only to admins, and never renders secrets (an empty secret field keeps the stored value). - /settings/save only accepts keys the settings form has; everything else (TTS command templates, model paths, ...) must be edited in settings.yml. Changing or adding conversion command templates from the browser, which chooses the programs the server runs, requires ALLOW_COMMAND_EDITS=true; the form shows them read-only otherwise. Numeric fields are validated (a blank field used to be saved as null and break config loading). - New auth_settings.allowed_users / allowed_domains restrict who may log in (checked at login, per request and for API bearer tokens). Admin and allowlist matching ignore emails the IdP marks as unverified. - The session key was generated per process when SECRET_KEY was unset, so with 4 gunicorn workers OAuth state checks failed randomly. A generated key is now persisted to config/.secret_key (0600) and shared. SESSION_COOKIE_SECURE=true sets the Secure flag on the session cookie. - Logout sends id_token_hint and client_id and URL-encodes the redirect, fixing RP-initiated logout with Keycloak (#20). - Workers pick up settings.yml changes on their next request instead of serving stale config until restart. - The index page only receives tool names and formats, not command templates. - LOCAL_ONLY API calls use the same identity as the UI, and /download, /job/{id} and /api/v1/jobs/status accept bearer tokens, so webhook download_url links and status polling work for API clients. - Removed the /test-websocket-notification debug endpoint; forward-auth returns 404 in LOCAL_ONLY mode instead of crashing. - Removed the second deep_merge definition, which shadowed the first and crashed when a merged value was None. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01MjPx3yn1u3MzoCV7DMYKwB
- Chunked uploads (the path the UI uses) ignored max_file_size_mb entirely.
The limit now applies across all chunks of an upload and when stitching.
- upload_id is validated strictly and chunk directories are namespaced per
user. An id such as ".." used to sanitize to "" and resolve to the shared
chunk root, and finalizing it deleted everyone's in-progress uploads.
- Finalize validates task type, output format, Whisper model and TTS voice
before stitching, so rejected requests leave no files behind.
- Whisper models are restricted to allowed_models on every path (UI,
batches, API, and inside the task). faster-whisper also accepts local
paths and arbitrary Hugging Face repo ids.
- TTS voice names are validated, so they can't be passed to the Piper
download CLI as options or paths.
- ZIP batches: limits on member count (max_zip_members, default 500) and
total uncompressed size (max_zip_uncompressed_mb); symlink entries and
__MACOSX/.DS_Store junk are skipped.
- File names keep their extension when the stem has no ASCII characters
("文件.pdf" used to become "pdf" and fail with a 500).
- ZIP parent jobs could end up stuck on "processing" when children
finished before dispatching was done; the parent now stays pending until
all children exist.
- /api/v1/process validates before saving (no orphaned uploads), writes
image-OCR results as .pdf, and callback_url from chunked API uploads is
now stored so the webhook fires.
- /convert-file and /api/v1/process rejected every tool whose id contains
'_' (ghostscript_pdf, ffmpeg_audio, ...); they now match known tool ids.
- A periodic task removes abandoned chunk uploads and extraction
directories of finished batches (STALE_UPLOAD_HOURS, default 6).
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01MjPx3yn1u3MzoCV7DMYKwB
Process handling - Tool output went to pipes that were only read after exit, so any tool printing more than ~64 KB (ffmpeg progress, calibre, docling) blocked until the timeout and the job failed as "timed out" (likely #21). Output now goes to temp files. - Cancelling a running conversion had no effect: the poll loop re-read the job inside one SQLite read transaction and never saw the cancellation, so the tool ran until its timeout. Status is now read from a fresh transaction, and a cancelled job is no longer overwritten as "failed". - Tools run in their own process group; cancel and timeout kill the whole tree (e.g. soffice.bin behind the soffice wrapper) and reap it. - The preexec hook logged from the forked child of a multi-threaded process, which can deadlock; limits are now computed in the parent and clamped to the hard limits. CHILD_CPU_LIMIT_SECONDS and CHILD_MEMORY_LIMIT_MB (0 disables) make them configurable. - TeX shell escape (\write18) is disabled for all child processes. Conversions - LibreOffice/docling write "<stem>.<ext>" into {output_dir}, which was the uploads directory: every conversion failed after a 30 s wait and left the result behind in uploads (#16). Such tools now get a private output directory whose result is moved into place, and LibreOffice gets a per-job profile so concurrent jobs don't silently hand off to another instance. - Ghostscript PDF presets (screen/ebook/printer/prepress/PDF-A) were silently dropped; they now map to -dPDFSETTINGS / -dPDFA. - pandoc txt/md/tex outputs failed (pandoc 3 has no such writer names); they now use plain/markdown/latex. - SoX "44k" presets produced 44000 Hz instead of 44100 Hz. - Tools whose program is not installed are hidden from the UI, reported on the settings page and rejected with a clear error. Housekeeping - The huey consumer now loads settings and runs the Whisper cache eviction thread itself (models are loaded there, so they were never unloaded). - Kokoro model downloads: one worker downloads, via .part + rename (all four gunicorn workers used to write the same files concurrently). - "Delete Processed Files" only covered the newest 100 jobs, and "Clear History" orphaned every file; both now cover all of the user's jobs and clearing history removes the files too. - Batch ZIP downloads are built in a worker thread in a spooled temp file instead of in memory on the event loop, with unique entry names and a proper Content-Disposition. - Job status notifications are only queued when WebSockets are enabled (the table grew on every progress update otherwise). - /api/formats/count reflects the active config and installed tools. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01MjPx3yn1u3MzoCV7DMYKwB
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01MjPx3yn1u3MzoCV7DMYKwB
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01MjPx3yn1u3MzoCV7DMYKwB
- Dropping a single file opened the Convert dialog with an empty format list (the list was filtered before the dialog's Choices widget was recreated), and later single-file drops showed the unfiltered list. The widget is now created first, and filtering only touches the widget it was asked to update instead of also replacing the main form's list. - A single .zip is a batch whose files can use any format, so it now gets the full list instead of only the academic Pandoc formats. - Selecting several files resets the main list to all formats. - run.sh no longer aborts when .env does not exist (#13). Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01MjPx3yn1u3MzoCV7DMYKwB
OCR language (#26) - New default language setting (ocr_settings.ocrmypdf.language, e.g. "eng" or "eng+spa") and a per-job language picker next to "Start OCR", filled from the Tesseract languages installed on the server (GET /api/v1/ocr-languages). Both PDF (ocrmypdf) and image OCR use it; /api/v1/process accepts ocr_language. Unknown or malformed languages are rejected. Spanish text such as "niño" was read as "nifto" with the fixed English model. History management (#25, #5) - Every finished job (including failed and cancelled ones) can be selected; "Delete Selected" removes the jobs, their files and, for ZIP batches, all sub-jobs (POST /jobs/delete). Running jobs are cancelled first so their tools are killed. "Download Selected" only takes jobs with a result. - Optional retention: app_settings.retention_days deletes finished jobs and their files after N days (0 = keep everything, the default). - Conversion tool timeouts can be edited on the settings page (#21). Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01MjPx3yn1u3MzoCV7DMYKwB
jobs.db, huey.db and app.log were written next to main.py. In the Docker image that is inside the container, so the job history was lost whenever the container was recreated, and the app directory must stay writable. DATA_DIR (default: the app directory, so manual installs are unchanged) moves them to a data directory; the Docker image points it at a volume. File logging is skipped with a warning if the log can't be opened. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01MjPx3yn1u3MzoCV7DMYKwB
- Converter memory limit uses RLIMIT_DATA instead of RLIMIT_AS. Calibre's PDF output renders with Chromium (QtWebEngine), which reserves far more address space than it uses and failed under the 4 GB address-space limit; RLIMIT_DATA still stops processes that actually allocate too much. - run.sh starts the worker with `python -m huey.bin.huey_consumer`: current huey releases install `huey_consumer`, not `huey_consumer.py`. - /health reports the image version and variant. - With ALLOWED_HOSTS set, loopback host names stay allowed so container health checks keep working (DNS rebinding arrives with its own host name). - New conversion tool "rsvg" (librsvg's rsvg-convert: SVG to PNG/PDF/PS/EPS), since resvg is not packaged for Ubuntu 24.04. - Requirements: removed packages the app never imports (opencv, bleach, mechanize, html5_parser, css_parser, the "dotenv" duplicate of python-dotenv; the last three are calibre's own dependencies, which apt installs) and marker-pdf from the CUDA set. The CUDA set is now the full set plus the CUDA 12 cuBLAS runtime used by CTranslate2. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01MjPx3yn1u3MzoCV7DMYKwB
The previous Dockerfile could no longer build as written (the CUDA stage installs python3.12-distutils/-pip, which don't exist for Python 3.12, and copies site-packages from the wrong path), used unpinned packages and ran everything as root. - One Dockerfile for all variants, selected with VARIANT=full|small|cuda, on Ubuntu 24.04 (Python 3.12 from the distribution, same base for CPU and CUDA images). full and small build for linux/amd64 and linux/arm64 (#14, #18); cuda for amd64. - Reproducible: base images pinned by digest, apt packages from a dated snapshot.ubuntu.com snapshot (amd64; there are no public snapshots of the arm64 ports archive), Python packages from hash-pinned lock files (locks/, generated by scripts/lock-requirements.sh), SOURCE_DATE_EPOCH for timestamps created during the build. Two --no-cache builds of the small image with rewrite-timestamp=true produced byte-identical images. - Wheels only (no compilers in the image); PyTorch for Docling is the CPU build from the PyTorch index instead of PyPI's CUDA build (~3 GB less). - The cuda image is the full image plus the CUDA 12 cuBLAS runtime that CTranslate2 loads (from the nvidia-cublas-cu12 wheel), and transcribes on the GPU when one is passed in (TRANSCRIPTION_DEVICE=auto). The old image shipped cuDNN 8 while current CTranslate2 needs CUDA 12 libraries (#5). - Runs as an unprivileged user. PUID/PGID (default 1000:1000, Unraid 99:100) select the uid/gid; mounted folders written by older root-run images are handed over on start. docker run --user also works. - Calibre can render PDFs in the container (QtWebEngine sandbox disabled inside the container, offscreen platform), fixing #9. - Job history database and log live in /app/data, models in /app/models (HF_HOME), so both can be kept on volumes across updates. - Tesseract language packs selectable with TESSERACT_LANGS (default: eng deu fra spa ita por nld pol) (#26); new rsvg-convert, cjxl, cjpeg and potrace are now actually installed. - Health check, OCI labels, whitelist .dockerignore, supervisord config in docker/ (worker started with python -m huey.bin.huey_consumer). - Optional build secret build_ca for building behind TLS-inspecting proxies. - docker/smoke-test.sh starts an image and checks the web UI, the worker, the non-root user, OCR languages, pandoc and LibreOffice conversions and, for full/cuda, the torch/docling and cuBLAS imports. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01MjPx3yn1u3MzoCV7DMYKwB
- .github/workflows/docker-publish.yml builds every variant on native amd64 and arm64 runners, smoke-tests each image, then pushes by digest and assembles the multi-arch tags. Pushing a tag vX.Y.Z publishes X.Y.Z, X.Y and latest plus the -small/-cuda equivalents and the floating small, cuda and latest-cuda tags (#5); pre-releases only get their version tags. Pull requests that touch the image build and test the small image; "Run workflow" can publish edge builds. Needs the DOCKERHUB_USERNAME and DOCKERHUB_TOKEN secrets. - Unraid templates for the CPU and CUDA images (#5), with PUID 99 / PGID 100 and appdata paths. - docs/docker.md: variants, volumes, PUID/PGID, GPU setup, local builds and build arguments, releasing (automated and manual buildx commands), reproducibility and how to verify it, updating locks/snapshots/base images, troubleshooting. - README: current image tags incl. arm64, updated install instructions. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01MjPx3yn1u3MzoCV7DMYKwB
…ease Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01MjPx3yn1u3MzoCV7DMYKwB
Tested by upgrading a real 0.4 install (published image, its compose file, edited settings, finished jobs) to the new image and rolling back. - Entrypoint: also re-own folders whose files (not just the folder) belong to someone else, e.g. a job history copied in as root; tolerate storage that refuses chown, and stop with a clear message when a folder is not writable instead of crash-looping on a Python traceback. - docker-compose.yml: keep the service name "web" from 0.4; renaming it left the old container running on the same port. - Keep publishing the X.Y-latest tag 0.4 users pinned. - Docs: "Upgrading from 0.4" (ownership, carrying the job history over, settings, reverse proxies, OIDC, tags, older converter versions, rollback). Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01MjPx3yn1u3MzoCV7DMYKwB
…logs - compute_type "auto" (new default and settings option) picks int8 on CPUs and float16 on GPUs. The CUDA image no longer forces it through TRANSCRIPTION_COMPUTE_TYPE, which made the settings page choice ineffective. - Failed uploads show the server's reason (e.g. file too large) as tooltip. - Silence Authlib's deprecation warning about its own httpx integration, printed once per process on every start. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01MjPx3yn1u3MzoCV7DMYKwB
- `scripts/docker-release.sh test 0.5.0` builds all variants for amd64 and arm64 (cuda: amd64) and pushes them as test, test-small and test-cuda. - `promote 0.5.0` gives the tested images their release tags with `buildx imagetools create`: nothing is rebuilt, the release tags point to the exact images that were tested. It refuses test images built for another version. - `tags 0.5.0` prints the release tags; the workflow now uses it too, so the tag scheme lives in one place. - Workflow: also triggers on tags without "v" and with two parts (0.5), the style of the existing releases; without Docker Hub secrets it builds and tests but skips publishing (with a warning) instead of failing. - docs/docker.md: step-by-step release from your machine (test, try, promote, tag) next to the GitHub Actions route. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01MjPx3yn1u3MzoCV7DMYKwB
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01MjPx3yn1u3MzoCV7DMYKwB
… tags - Export with unpack=false: with the default "docker" builder on the containerd image store, rewrite-timestamp otherwise fails with 'exporter option "rewrite-timestamp" conflicts with "unpack"'. - The closing hints list the tags that were actually pushed/promoted instead of assuming the full image was built. Tested end to end against a local registry: small image for amd64 and arm64 built and pushed as test-small (with provenance and SBOM), smoke tests passed on both architectures, promote gave 0.5.0-small, 0.5-small and small the same index digest. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01MjPx3yn1u3MzoCV7DMYKwB
ENABLE_WEBSOCKETS was off on both sides, so the connection manager, the notification table and its 10 Hz queue processor, the /ws/jobs endpoint and the browser's reconnect logic never ran. The browser half only worked by accident: pollForJobUpdates() read a `let` that was declared further down the file. The job history keeps using polling, which the next commits make incremental. Existing databases keep their (unused) notifications table. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_018LReNFoeaXg1g9e3H4E98S
… voices - GET /jobs?since=<timestamp> returns only jobs created or changed since then, so the page no longer downloads its whole history every 1.5 s. Without `since`, the 100-job limit counts top-level jobs and each ZIP batch comes with all its sub-jobs (a big batch used to push its own parent out). - Indexes for the history and its polling (user_id+created_at, user_id+updated_at) and for download lookups; they are also added to existing databases on startup. - ZIP batch progress is counted with one GROUP BY instead of loading every sub-job each time one of them finishes. Batch conversions skip files the chosen tool does not accept instead of failing them. - Image OCR runs Tesseract once per page for both the PDF and the text (it ran twice), converts one TIFF frame at a time instead of holding all of them in memory, can be cancelled between pages, and merges pages with pypdf (PyPDF2 is no longer imported). - PDF OCR stops extracting preview text after 2000 characters instead of reading every page of the result. - The TTS voice list runs in a thread instead of blocking the event loop (offline, piper's CLI could stall a worker for ~90 s), fetches Piper's catalogue with a timeout, also lists downloaded voices, groups voices by language, and is cached in DATA_DIR for all workers: a day, or 5 minutes when the listing was incomplete. - Batch ZIP downloads store already-compressed files instead of deflating them again, and accept a form post so the browser can stream them. - Single-chunk uploads are moved into place instead of copied. - gzip for pages, scripts, styles and JSON (not for /download). Static URLs carry a content hash, so they are cached for good and an upgrade can't leave browsers running old scripts. - huey workers check the queue at least once a second when idle (the default backoff let a job wait up to 10 s before it started). - Dropped per-conversion gc.collect(), a duplicate status write after image OCR and an unused schema validation per created job. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_018LReNFoeaXg1g9e3H4E98S
- Jobs that were running when the background worker stopped (container
restart, crash, out of memory) stayed "processing" forever, and the page
kept polling them: huey does not re-run a task it had already dequeued.
On startup the worker now marks such jobs failed (only jobs last updated
before it started, so it never touches its own), removes their inputs,
sends their webhooks and settles the ZIP batches they belonged to.
Batches cut off while unpacking are failed too; queued jobs are kept.
- Webhook callbacks are retried on connection errors, 5xx and 429
(3 attempts, 2 s and 4 s apart); the DB session is closed before the
request instead of staying open through it.
- The periodic cleanup also removes partial files ("*.tmp-<uuid>") left
in the upload and processed folders by interrupted uploads/conversions.
- A Whisper model is no longer unloaded while it is still transcribing
(the next job loaded a second copy). The two copy-pasted transcription
loops are one, and progress stays below 100 until the job completes.
- "Clear history" cancels running jobs first, like "Delete selected".
- Cancelling returns the updated job, so the page can show it at once.
- Job endpoints that only do blocking DB and file work are plain `def`
now, so FastAPI runs them in its thread pool instead of on the event
loop, where a write waiting for the SQLite lock stalled every request.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_018LReNFoeaXg1g9e3H4E98S
Job history - Polls only the jobs changed since the newest update it has seen (GET /jobs?since=...), backs off from 1.5 s to 6 s while nothing changes, pauses in background tabs and catches up as soon as the tab is visible. Polling stops when no job is pending or processing. - Only rows whose job changed are touched, and only the cells that changed: selections, open details and the progress bar's animation survive a poll. Fixes the File Size column never showing the output size until a reload, and sub-jobs added to an expanded batch staying hidden. - Cancelling shows the cancelled state immediately. - The bottom status shows the real state (Live updates / Up to date / Connection lost – retrying) instead of a fixed "Polling active". Uploads - Files go through a queue with at most 3 uploads at a time, so the first files finish and start processing sooner; the file input is cleared at once and more files can be queued right away. - Chunks are retried with backoff after network errors, 5xx and 429. - Files that are too large, not allowed, or not an image/PDF for OCR are rejected before uploading (the server refused them only after the full upload). Queued and running uploads can be cancelled, failed ones dismissed, and closing the tab mid-upload asks for confirmation. Other - Output formats are computed in the browser from the embedded tool list (no request per selection); with several files, only formats that work for all of them are offered, and unsupported types say so. - Selectors are created once; submitting no longer rebuilds them all and re-downloads the TTS voice list (which also reset the model choice). - The TTS list no longer shows a voice named "error" when no engine is installed. - "Download Selected as ZIP" posts a form into a hidden frame, so the browser streams the archive to disk instead of holding it in memory. - Escape closes the drop dialog; a failed history load is retried. - The huey worker skips job recovery when the database does not exist yet (fresh install: the web server creates it). Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_018LReNFoeaXg1g9e3H4E98S
Also guards the cancel response against a job deleted while it was being cancelled. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_018LReNFoeaXg1g9e3H4E98S
The test step always pushed test, test-small and test-cuda, so a nightly build replaced the images waiting to be promoted. TEST_TAG=nightly pushes nightly, nightly-small and nightly-cuda instead (promote reads the same tag); the default stays "test". Checked with a stand-in docker command: the test and promote steps with TEST_TAG=nightly only touch the nightly tags, the default still pushes test-*. Invalid tag names are rejected. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_018LReNFoeaXg1g9e3H4E98S
Add version
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.
Updating to 0.5.0 fixes major bugs and issues, 0.5.1 brings additional performance and stability.