devops
Technical notes on web development, DevOps, and AI integration.
101 articles
- 15:55security
A Gitleaks Crash on a Multi-File Commit, and a Supply-Chain Gate Was Born
The secret-scanner hook crashed on a multi-file commit. One missing flag in the hook definition, and it ended with checksum-pinned CI.
TL;DR: The gitleaks system hook kept crashing on multifile commits because it mishandles the file list. Instead of waiting for a fix I switched to a local hook that runs a checksum verified binary on the staged diff. I apply the same strict pinning in CI with hashed downloads and minimal permissions accepting manual updates for stronger supply chain security.
#github-actions#security#ci-cd - 11:32devops
Do Not Use Absolute URLs for Plane Comment Image Embeds
Image embeds in Plane API comments fail hard on absolute URLs (401). Verified pipeline: in-container helper, image-component node, raw asset_id.
TL;DR: Using absolute URLs for Plane comment images always returns 401 because assets must load through an authenticated local proxy. Instead create the comment first, upload via the in-container helper, and embed with the native image-component node using only the raw asset ID. Remember updates replace all HTML so resend full content and verify rendering in the browser not with curl.
#plane#mcp#docker - 08:23devops
Bypassing the MCP Limit: Images in Plane Comments
The Plane MCP server has no upload action and the SSRF guard rightly rejects private URLs. The self-host answer: drop one level and create the FileAsset inside the container.
TL;DR: The Plane MCP server can't upload files and its SSRF guard correctly blocks loopback URLs and internal API keys. The workaround is a tiny Python script that copies the image into the container and uploads it to MinIO via Django shell. Then you patch the comment's HTML with the returned URL, which renders in the UI but stays private.
#plane#mcp#docker - 04:18testing
Unit Testing Whose Contents Are Not Just Unit Tests
One script per test tier: vet, integration against real MySQL, a smoke gate, up to Playwright E2E.
TL;DR: Scattered manual tests were often skipped, so they were consolidated into four strict scripts for backend, frontend, E2E and smoke. Backend handles Docker and runs integration tests serially on real MySQL, while smoke validates deployment endpoints and E2E wraps Playwright with retries. README now defines what to run when, so a passing backend script signals the shared team contract.
#testing#bash#go - 03:16devops
Flip the DB Sync Direction, Flip Every Safety Assumption
Flipping db-push into db-pull is more than reversing arrows: local backs up first, the dump gets inspected before trusted, backtick rules change over SSH.
TL;DR: Reversing the sync direction puts your local database at risk, so the guard sequence has to flip. The script first backs up local data for rollback, then takes a consistent staging dump and checks size, tables and completion before wiping anything. It avoids SSH backtick issues and strictly compares row counts afterward, preferring a hard failure over a half-finished import.
#mysql#bash#docker - 02:35devops
Checking the gate with one command
What used to take four MCP round trips and an 86KB payload at session start is now one read-only script with semantic exit codes.
TL;DR: Checking status used to need four heavy MCP calls, and caching only created stale-data headaches. The fix was a lightweight read-only script that runs four checks in parallel with clear exit codes for ready, drift, or infra trouble. It handles quirks like Cloudflare blocks and rate limits while staying local for quick iteration and always-fresh data.
#python#plane#devops - 14:17devops
The document mirror moved offices: work items to Plane Pages
The document mirror moved off the kanban board and into Plane Pages, and the storage contract forced a full identity redesign.
TL;DR: The mirror moved from Work Items to Pages, which preserves HTML comments instead of sanitizing them, forcing a new identity design. Pages are flat with no folders, need full replacement on update, and require archiving before delete with verify-after-write. Sync now trusts content hashes, allows two-way edits only for meeting notes, and keeps library docs one-way with divergence alerts.
#plane#self-hosting#git - 23:25devops
Done on the tracker is a claim; the proof lives in Git
A card flipped to Done with nothing behind it. Now a work item only counts as done when its evidence hashes actually resolve in the repository.
TL;DR: A Done label means nothing without Git proof, so I stopped trusting tracker status alone. I built a gate that only allows Done when visible evidence lines with commit hashes and tags actually resolve in the repo. Strict rules like WIP limits, date handling, and separating docs keep the board honest and verifiable.
#plane#self-hosting#git - 23:25backend
Two-way document sync: the trustworthy tool refuses precisely
A pull returned different content than my push, with no error. The fix was not a smarter parser but a precise map of what the sync refuses to do.
TL;DR: My Markdown quietly broke after a pull because Plane sanitizes HTML and drops unsupported formatting without warning. Instead of forcing acceptance, I built a sync engine that refuses risky work, storing Markdown safely and using stripped text as the stable identity. It classifies every outcome into six explicit statuses and fails loudly rather than guessing, prioritizing data integrity over convenience.
#plane#self-hosting#sync