AI 资讯
The Turborepo + Bun + Biome stack behind a 40-package monorepo
Forty packages, one maintainer, and no ESLint config anywhere in the repo. That is not a boast - it is the direct result of a decision made early: every tool in the toolchain has to earn its place by governing all forty packages from one config file, not forty. The repo is flare-engine , a modular 2D engine for React Native + Web (animation, gamification, interactive UI, with games as showcases - not a game engine, not a Unity or Godot competitor). The stack behind it is Turborepo Bun Biome , and this post is the actual setup: the real turbo.json , the real biome.json , the real CI guardrail, straight from the repo (trimmed only where a config is long, and I say so where I trim), not a starter template's idealized version. Four binaries, four root configs - turbo.json , biome.json , tsconfig.base.json , and the Changesets config - each governing all forty packages at once (Bun's own "config" is just the workspaces array in the root package.json ). Plus a CI step that fails the build the moment a package imports something it shouldn't. That is the whole story, and I want to show you the files, not describe them. Four binaries, not twelve config files The thesis is narrow: a solo maintainer can keep forty packages honest only if there is exactly one config of each kind, and every package extends it rather than declaring its own variant. Twelve packages each with a slightly different ESLint config is not a monorepo, it is twelve monorepos wearing a workspace file as a costume. Here is the root package.json that runs all of it - Bun workspaces (not pnpm; that distinction matters and I will say it again below), the script table every package leans on, and the pinned package manager: // C:\_PROG\flare-engine-workspace\flare-engine\package.json { "name" : "flare-engine" , "version" : "0.0.0" , "private" : true , "workspaces" : [ "packages/*" , "benchmarks" , "apps/*" ], "scripts" : { "build" : "turbo build" , "test" : "turbo test" , "lint" : "turbo lint" , "typecheck" : "t
AI 资讯
A pending-plugin-count badge on the 🔌 button — reusing the dashboard cache instead of doubling state
A client asked: " After I run a cross-site update check, can each site show — right in the site list — how many plugin updates are still pending? " Visually the answer was obvious: a small red badge on the top-right of the 🔌 plugins button, like an unread-notification count. Easy to specify. The harder question was where the data comes from . We could have added a fresh API endpoint and a new cache to hold "pending count per site." But doing that would have doubled state management , and we already had a cache that knew this. We routed through the existing one. Here's the reasoning behind that decision. Reuse the dashboard cache as the data source The cross-site updates dashboard (the one we wrote about in killing the 24.5-second silence with a cache-first design ) already kept each site's pending plugins in a localStorage-backed state called _updatesDashState . Its shape: _updatesDashState = { sites : [ { site_id : " abc... " , plugins : [ {...}, {...}, {...} ] }, { site_id : " def... " , plugins : [ ... ] }, ], total_pending_count : 12 , loadedAt : 1748600000000 , } Look up by site_id , take plugins.length , and you have the badge's number. No new API, no new cache. The data that powers the cross-site dashboard is also the data that powers the site-list badge. The win of not adding state is quiet but real: When a maintenance run invalidates _updatesDashState , the badge disappears automatically (no sync code to write) The TTL (originally 7 days; later extended to 30 days with partial invalidation ) inherits from the existing design The badge and the underlying count can't drift — there's no second copy to fall out of step There's always a temptation to spin up a new endpoint for a new UI element. The rule we settled on: if the existing state answers it, don't add more. Attaching the badge — consolidate into helpers Both the list view and grid view need the same badge on the 🔌 button, so the logic lives in helpers. function _getPendingPluginCountForSite ( siteId )
AI 资讯
Deploying SFTPGo as an Azure Storage SFTP Alternative on Linux
Azure Storage SFTP is Microsoft's managed file transfer service on top of Azure Blob Storage, convenient, but billed continuously per endpoint (roughly $0.30/hour, ~$220/month) and tied to Azure AD. SFTPGo is an open-source file transfer server offering SFTP, FTP/S, and WebDAV with pluggable storage backends (local disk, Azure Blob, S3-compatible, GCS) and no per-endpoint charge. This guide deploys SFTPGo with Docker Compose and Traefik, sets up user auth (password + SSH key + 2FA), connects S3-compatible object storage, and covers the migration path from Azure Storage SFTP. By the end, you'll have a self-hosted file transfer server with the same capabilities at zero endpoint cost. Azure Storage SFTP → SFTPGo Mapping Azure Storage SFTP SFTPGo Equivalent Notes SFTP Endpoint SFTPGo SFTP Server Configurable port, default 2022 Azure Blob Storage Azure Blob backend Native support; point at the same container, no migration needed Azure AD Authentication LDAP/OIDC plugin External identity provider via plugin Local Users Web UI / REST API user management Hierarchical Namespace Virtual directories No HNS requirement Azure Monitor Built-in logging + webhooks/syslog Prerequisite: Linux server with Docker + Compose, a DNS A record for your domain, and (if migrating) an existing Azure Storage account with SFTP enabled plus the Azure CLI installed locally. Deploy with Docker Compose 1. Create the project directories: $ mkdir -p ~/sftpgo/ { data,config } $ cd ~/sftpgo 2. Create the environment file: $ nano .env DOMAIN = sftp.example.com LETSENCRYPT_EMAIL = admin@example.com 3. Create the Compose manifest: $ nano docker-compose.yml services : traefik : image : traefik:v3.6 container_name : traefik command : - " --providers.docker=true" - " --providers.docker.exposedbydefault=false" - " --entrypoints.web.address=:80" - " --entrypoints.websecure.address=:443" - " --entrypoints.web.http.redirections.entrypoint.to=websecure" - " --certificatesresolvers.letsencrypt.acme.httpchallenge=tr
AI 资讯
X says top accounts steal videos from other users as it announces new video tools
Nikita Bier, X's head of product, said in a post on Monday that "[m]any videos from top accounts are simply stolen from other users, sometimes 5 years after they originally went viral," while noting that videos on the platform "make up close to half the impressions on X." According to Bier, X is launching a […]
AI 资讯
Replication Monitoring — HA Dashboard
HA dashboard cho Postgres replication: vì sao "có replica" không đồng nghĩa "đang HA", và cách để replica chết không âm thầm Streaming replication trong Postgres là một tunnel WAL từ primary sang standby: primary chạy một walsender cho mỗi standby, standby chạy một walreceiver nhận WAL và một startup process replay. Đau nhất trong vận hành không phải setup — mà là replica đã ngắt nhiều giờ mà không ai biết, tới lúc primary chết mới phát hiện HA thực ra là single-node từ tuần trước. HA dashboard là tập metric + alert được rút ra từ pg_stat_replication , pg_stat_wal_receiver , và pg_replication_slots để ép mọi trạng thái xấu — replica disconnect, replay đứng, slot pin WAL, sync standby biến mất — thành tín hiệu nhìn thấy được trước khi biến thành sự cố. Cơ chế hoạt động Trên primary, mỗi standby đang kết nối tạo ra một backend loại walsender — Postgres đọc WAL từ pg_wal/ (hoặc từ WAL buffers khi còn nóng) và stream qua replication connection. Trên standby, walreceiver nhận từng WAL record, ghi vào pg_wal/ local, fsync (tùy synchronous_commit ), rồi startup process apply record lên shared buffers — đây chính là replay. Bốn LSN xuất hiện trong luồng này và tương ứng với bốn cột trong pg_stat_replication : sent_lsn — byte cuối cùng primary đã gửi qua socket. write_lsn — byte cuối cùng standby đã write() vào OS page cache. flush_lsn — byte cuối cùng standby đã fsync xuống đĩa. replay_lsn — byte cuối cùng standby đã replay vào shared buffers (dữ liệu đã "thấy được" trên standby). Postgres docs quy định replay_lsn <= flush_lsn <= write_lsn <= sent_lsn <= pg_current_wal_lsn() — bốn "vạch" này chính là bốn nhịp của lag. Ba cột write_lag , flush_lag , replay_lag (kiểu interval ) là thời gian mà standby chậm hơn primary tương ứng với ba mốc write/flush/replay — được đo qua feedback message định kỳ từ standby. -- Trên primary: bức tranh đầy đủ cho một HA dashboard SELECT application_name , client_addr , state , -- streaming | catchup | startup | backup | stopping sync_state , --
AI 资讯
Write-Ahead Logging — WAL Fundamentals
WAL: vì sao Postgres bắt buộc ghi log trước data file, và lý do pg_wal/ đầy đĩa làm cluster ngừng nhận write WAL (Write-Ahead Log) là cơ chế durability lõi của Postgres: mọi thay đổi đối với heap, index, free-space map, visibility map đều phải được ghi xuống WAL và fsync trước khi data file tương ứng được phép flush ra đĩa . Nguyên tắc này, mô tả trong Postgres docs chương "Reliability and the Write-Ahead Log", là cái cho phép một transaction đã COMMIT thoả ACID-D dù OS crash hoặc mất điện ngay sau đó. Dev gặp WAL trong việc thật không phải vì cú pháp khó: gặp khi pg_wal/ đầy đĩa do một replication slot bị quên dọn, Postgres dừng nhận write với PANIC: could not write to file ... No space left on device , hoặc khi crash recovery sau OOM kéo mười mấy phút làm health check fail và load balancer cắt traffic. Cơ chế hoạt động Postgres không ghi thẳng vào data file mỗi khi có INSERT / UPDATE . Trang 8KB (heap page, index page) sống trong shared_buffers ; mỗi thay đổi tạo ra một WAL record mô tả delta đó (record type, relfilenode, block number, payload), append vào wal_buffers — một vùng shared memory nhỏ trước khi xuống đĩa. Tại thời điểm COMMIT , backend gọi XLogFlush() để write + fsync WAL tới hết byte chứa commit record; chỉ sau khi fsync trả về, Postgres mới ghi commit bit vào pg_xact và reply OK về client. Data page bẩn ở lại trong shared_buffers ; checkpointer sẽ flush chúng ra data file sau, không gắn với từng commit. WAL được tổ chức thành segment file kích thước cố định trong $PGDATA/pg_wal/ , mặc định 16MB mỗi segment (cấu hình lúc initdb --wal-segsize ). Vị trí trong WAL là LSN (Log Sequence Number) — số 64-bit, in dạng XXXX/XXXXXXXX , thực chất là byte offset từ đầu WAL của cluster. LSN tăng đơn điệu và là "đồng hồ" duy nhất Postgres tin cậy cho thứ tự ghi. -- Quan sát LSN tiến lên sau mỗi ghi SELECT pg_current_wal_lsn (); -- vd: 0/1A2B3C40 INSERT INTO t SELECT g FROM generate_series ( 1 , 1000 ) g ; SELECT pg_current_wal_lsn (); -- 0/1A2BE018 SELECT pg_wal_ls
AI 资讯
Lock Monitoring — Production Lock Analysis
Production lock analysis: vì sao pg_stat_activity một mình không đủ, và join với pg_locks mới ra root cause Lock contention trong Postgres hiếm khi báo bằng error — nó báo bằng wait_event_type = 'Lock' ở pg_stat_activity và bằng latency tăng từ phía application. Khi một incident xảy ra ("API treo, không ai biết tại sao"), thứ team cần trong 60 giây đầu là một bức tranh: PID nào đang đợi, đợi lock loại gì trên object nào, bị block bởi PID nào, PID block đó đang chạy query gì và đã giữ transaction bao lâu . pg_stat_activity một mình chỉ trả lời được nửa câu hỏi ("ai đang đợi"); pg_locks một mình chỉ trả lời nửa còn lại ("ai giữ gì"). Phải join hai view này — và bám theo pg_blocking_pids() — để dựng được blocking tree. Không có dashboard cho luồng dữ liệu này là lý do điển hình một production freeze kéo dài 30 phút thay vì 3 phút: incident commander phải mò ad-hoc bằng psql , gõ sai query, miss idle in transaction đang giữ AccessExclusiveLock của một migration nửa đời trước. Cơ chế hoạt động pg_locks là một view phơi nội dung trực tiếp của shared lock manager trong shared memory. Mỗi dòng là một lock request (đã granted hoặc đang chờ) thuộc một backend. Theo Postgres docs phần "System Views → pg_locks", các column then chốt: locktype ( relation , transactionid , tuple , virtualxid , advisory ...), relation (OID — join pg_class ), transactionid , virtualtransaction , pid (backend PID), mode ( AccessShareLock , RowExclusiveLock , ShareUpdateExclusiveLock , AccessExclusiveLock ...), granted (bool), fastpath (lock đi qua fast-path tránh shared lock manager), và waitstart (timestamp bắt đầu chờ — bổ sung sau v14, hữu ích để đo lock wait time mà không cần snapshot diff). pg_stat_activity là view phơi trạng thái runtime của mỗi backend: pid , usename , datname , application_name , client_addr , backend_start , xact_start , query_start , state ( active , idle , idle in transaction , idle in transaction (aborted) ), wait_event_type , wait_event , backend_xid , backend_xmin , qu
开源项目
X adds a video editor to encourage creators to post original content, not stolen reposts
X is rolling out a new video editor and recorder for iOS with multilingual captions, green-screen effects, and other editing tools.
开发者
Google Search lets creators know more about their reach
Google is going to give content creators and website owners a better idea of how people find their social media profiles and YouTube content through Search. With a new feature in the Google Search Console called "platform properties," Google says that you'll be able to "easily track which search terms lead people to your Instagram, […]
AI 资讯
The LLM narrates. The code decides.
Most of the "AI for observability" work I see right now hands the language model the judgment. I think that's backwards. Feed it the alert, feed it some metrics, ask it what's wrong, what should be done, and let it make the judgement call. Based on my experience working with language models, I decided that inverting the process provides better results. The short version: in my alerting pipeline, the set of allowable classifications is fixed in deterministic Python, and the model has to pick from it. The LLM's only job is to turn a structured verdict into an easily digestible sentence. It never decides whether something is bad, how bad it is, or what category of problem it is. It narrates within a decision space the code has already locked down. TL;DR: Instead of letting an LLM decide what's wrong with an alert, I let deterministic Python make every operational decision and restrict the model to explaining the result in plain English. The code classifies, validates, and aggregates; the LLM only narrates. That keeps the data consistent, prevents hallucinated classifications, and ensures the monitoring pipeline continues working even if the model fails. The problem I run a small managed monitoring service. Alertmanager fires, a webhook lands, and historically that webhook produced a line like HighMemoryUsage on host web-vm, severity warning, which is accurate, but not terribly helpful. The person reading it still has to know what HighMemoryUsage implies, whether this host always runs hot, and whether to care. I wanted plain-English context attached to the alerts without altering the alert delivery process. The obvious move was to throw the whole alert at an LLM and ask it to explain. I tried that in the first iteration of this experiment, expecting it to be somewhat accurate, but not entirely reliable, and it did not disappoint, the model was confidently inconsistent. The same alert, fired three times, produced three different "root cause" categories. One run called a
AI 资讯
How to Use Stream Analyzers for Digital TV Broadcasting: A Practical Guide
Part 1: Solving GOP Structure and Compatibility Issues Operating a digital television network isn't just about keeping channels on air—it's about maintaining quality that viewers expect and troubleshooting issues before they escalate. When something goes wrong in live broadcasting, every second counts. But how do you quickly pinpoint whether the problem lies in encoder settings, transport stream structure, or temporal metadata? This is where specialized stream analysis tools become essential. In this series of articles, we'll walk through real-world scenarios that broadcast engineers face daily and show practical approaches to diagnosing and resolving them. When File Analysis Becomes Critical While live monitoring catches issues as they happen, file-based analysis is your diagnostic microscope. Here's the typical workflow: something breaks in production, engineers capture a few minutes of the problematic stream, and now they need to understand exactly what went wrong. File analyzers serve three primary purposes: Troubleshooting: Identifying the root cause of broadcast issues Encoder optimization: Fine-tuning compression settings Quality control: Validating compliance with standards and specifications Let's explore how this works in practice with actual tools and techniques. The GOP Structure Problem Here's a scenario every broadcast engineer has encountered: legacy set-top boxes or older TV models suddenly can't play your stream. The audio works, video starts and stops, or you see freezing. The culprit? Often, it's the GOP (Group of Pictures) structure. H.264 has been around since 2003—over 20 years. Almost everything supports it, yet you'll still find legacy equipment that struggles with certain configurations. Specifically, the number of B-frames can make or break compatibility. Why B-frames matter: They enable lower bitrates while maintaining quality by increasing encoding complexity through bidirectional prediction. But this comes at a cost—a more complex refere
开发者
How to Monitor Website Changes Automatically (Visual Diff Tutorial)
How to Monitor Website Changes Automatically I run a few websites and need to know immediately when something breaks. A CSS regression, a broken layout, a missing section. Manual checking doesn't scale, and text-based monitoring misses visual issues. The {{screenshot-diff}} on Apify takes two screenshots and produces a pixel-level comparison with an overlay showing exactly what changed. How It Works Take a baseline screenshot of the correct state. Then take a current screenshot of the live page. The actor compares pixel by pixel and returns a diff image with changed pixels highlighted, plus a percentage telling you how much changed. import requests , time API_TOKEN = " YOUR_APIFY_TOKEN " def capture_screenshot ( url ): resp = requests . post ( " https://api.apify.com/v2/acts/weeknds~website-screenshot-api/runs " , headers = { " Authorization " : f " Bearer { API_TOKEN } " }, json = { " url " : url , " fullPage " : True } ) run_id = resp . json ()[ " data " ][ " id " ] time . sleep ( 15 ) items = requests . get ( f " https://api.apify.com/v2/acts/weeknds~website-screenshot-api/runs/ { run_id } /dataset/items " , headers = { " Authorization " : f " Bearer { API_TOKEN } " } ). json () return items [ 0 ][ " screenshotUrl " ] def compare_screenshots ( baseline_url , current_url ): resp = requests . post ( " https://api.apify.com/v2/acts/weeknds~screenshot-comparison-tool/runs " , headers = { " Authorization " : f " Bearer { API_TOKEN } " }, json = { " baselineImageUrl " : baseline_url , " currentImageUrl " : current_url , " threshold " : 0.01 } ) run_id = resp . json ()[ " data " ][ " id " ] time . sleep ( 10 ) items = requests . get ( f " https://api.apify.com/v2/acts/weeknds~screenshot-comparison-tool/runs/ { run_id } /dataset/items " , headers = { " Authorization " : f " Bearer { API_TOKEN } " } ). json () return items [ 0 ] baseline = capture_screenshot ( " https://mysite.com " ) current = capture_screenshot ( " https://mysite.com " ) result = compare_screenshots ( b
科技前沿
People Used to Control Machines. They Don’t Anymore
In a world regulated by devices, humanity has become disconnected from the physical world—from stick-shift cars to postcards.
AI 资讯
Every change to an Entra extension is a Control Plane event: the monitoring contract
Parts 1 and 2 of this series ( Microsoft Entra extensibility is a gift. It is also Control Plane. and Securing the code that decides who Entra trusts ) made two static decisions. Where the code lives: a dedicated Control Plane subscription, directly under the root management group or under a dedicated Control Plane management group, never in the platform identity subscription or an application landing zone. What credential it uses to call out: a managed identity by default, federated identity credentials when the call must leave Azure, certificates as a tolerated middle step, and static symmetric keys never. Both decisions are one-time. You make them, you walk away, you do not touch them for months. The third decision is not like that. It is continuous, and it is the one most teams quietly skip: how do you know the deployed code on that Function App is still the code your reviewers approved? How do you know the Logic App workflow definition has not been rewritten since last Tuesday? How do you know nobody added a federated identity credential to the managed identity at 3 a.m. on a Saturday? The answer is monitoring. Not "we have Log Analytics turned on." Monitoring with a specific operating contract attached. The posture inversion For most Azure workloads, the default operating posture is reasonable trust. Engineers deploy. Pipelines run. Configuration drifts a little. The team reviews changes weekly. Anomalies are caught eventually. For a Microsoft Entra extension, that posture is wrong. The default has to be inverted. Once an Entra extension lands in production, every change to it is suspicious by default. Not "needs review." Not "let's check first." Suspicious. The default state of an alert firing on a Function App that hosts a custom claims provider is "the SOC is investigating, prove this was approved." If you cannot prove the change was approved within the team's response SLA, the change is treated as an incident and rolled back. That posture is harsh on purpo
开发者
How HubSpot Scaled Semantic Search to 20 Billion Vectors
SaaS software vendor HubSpot has described how its semantic search platform grew from a proof of concept into an internal service that now manages more than 20 billion vectors across 38-plus teams. The company says the system now supports agents, RAG, and contact deduplication, and that the increase in agent usage has made retrieval quality and latency more important than before. By Matt Saunders
AI 资讯
OWASP Top 10 #1: Understanding Broken Access Control (Beginner's Guide)
Disclaimer: I'm currently learning web security through OWASP and PortSwigger Web Security Academy. These are my beginner-friendly notes rewritten as a blog to help reinforce my understanding. If you're just starting out, I hope this makes the topic easier to understand. What you'll learn: In this article you'll learn: ✔ What Broken Access Control is ✔ Vertical Privilege Escalation ✔ Security by Obscurity ✔ Parameter-Based Access Control ✔ Platform Misconfiguration ✔ Horizontal → Vertical Escalation ✔ IDOR ✔ Lessons learned from PortSwigger labs Concept Map: What is Broken Access Control? Broken Access Control happens when a user is able to access data, pages, or perform actions that they are not supposed to . Why should you care? Broken Access Control has ranked #1 in the OWASP Top 10 (2021) because it can expose sensitive data, allow privilege escalation, and let attackers perform actions they should never be able to perform. Think of a website with two types of users: Normal User Admin A normal user should only be able to view their own profile and perform basic actions. An admin, however, can manage users, delete accounts, change settings, etc. If a normal user somehow gains access to those admin features, that's Broken Access Control . In interview terms: Broken Access Control is the failure to properly enforce authorization, allowing users to perform actions beyond their intended permissions. 1. Vertical Privilege Escalation Vertical Privilege Escalation means moving up the permission hierarchy. Example: Normal User ↓ Admin A normal user should never be able to become an administrator. 1.1 Unprotected Functionality One of the simplest forms of Broken Access Control is Unprotected Functionality . Imagine a website has an admin page: /admin The developer removes the Admin button from the normal user's dashboard. Problem solved? No. If a user manually visits: /admin and the server doesn't verify whether they're actually an admin, the page opens. The mistake here
开发者
INTO Coding...
Hi folks! This is Mark Tony , a fresher to this field of technology from the UG Physics background. In a way, I'm pursuing my desire which I missed during my college days. My new venture begins along with @payilagam_135383b867ea296 Where I'm doing my Full stack developer course right now. I'm excited and enthusiastic about learning and becoming a developer. Dev community kick starts my journey 😉😊
AI 资讯
A "days since last maintenance" badge — color-coding staleness across many sites
When you maintain a number of WordPress sites, showing the "last maintenance date" in the site list is the obvious move. A column of dates like 2026-05-21 . But in actual use, that alone falls short. A client put it well: "Besides the last maintenance date, it'd help to also show how many days have passed . And it'd be even better if the color changed at 15 / 30 / 60 days so I can see the risk level ." This post walks through that step — from "absolute date" to "relative elapsed days + color" — including the small design details. Why a date alone isn't enough An absolute date like 2026-05-21 is precise, but it pushes the "difference from today" calculation onto the user's head . Fine for five sites; as the managed set grows, reading "which ones are getting neglected" off a column of dates gets hard. The point of a maintenance inventory is to grasp which sites need attention at a glance. If so, what you should surface is less the absolute date and more the relative quantity — " how many days since the last maintenance " — and ideally let color convey "how many days until it's risky." The client's request landed exactly on this "absolute → relative + risk" shift. Four-tier color coding We went with four tiers by elapsed days. A small badge like (15 days ago) sits right after the last-maintenance date, and the color changes by threshold. Elapsed tier color meaning 0–14 days fresh green recently maintained, fine 15–29 days normal gray standard 30–59 days warn amber needs attention 60+ days danger red needs action green → gray → amber → red — just scrolling the list, "lots of red here" or "a cluster of sites I haven't touched lately" jumps out visually. The badge also gets a hover tooltip ("N days since last maintenance") to back up the number's meaning. Consolidate into helper functions The display logic is called from multiple places (list view, grid view), so scattering inline day calculations would be a DRY violation. We consolidated into a set of helpers. // Returns
AI 资讯
How to criar Dockerfiles eficientes com multi stage builds
Multi stage builds sao uma das melhores features do Docker para manter imagens pequenas e organizadas. Vou mostrar como aplicar isso em um projeto Python real. Crie um arquivo app.py simples: # app.py def main(): print("Hello from a multi stage build") if __name__ == "__main__": main() Agora crie o Dockerfile sem multi stage: FROM python:3.12-slim WORKDIR /app COPY requirements.txt . RUN pip install --no-cache-dir -r requirements.txt COPY . . CMD ["python", "app.py"] Essa imagem inclui o pip, o cache do pip e ferramentas de build que nao precisamos em producao. O resultado e uma imagem maior que o necessario. Com multi stage builds separamos o ambiente de build do ambiente final. Veja o mesmo Dockerfile com dois stages: FROM python:3.12-slim AS builder WORKDIR /app COPY requirements.txt . RUN pip install --no-cache-dir -r requirements.txt FROM python:3.12-slim WORKDIR /app COPY --from=builder /usr/local/lib/python3.12/site-packages /usr/local/lib/python3.12/site-packages COPY . . CMD ["python", "app.py"] O primeiro stage instala as dependencias. O segundo stage copia so o que importa. O resultado e uma imagem final muito menor. Para construir e ver o tamanho: docker build -t minha-app . docker images | grep minha-app Para linguagens compiladas como Go o ganho e ainda maior. Veja um exemplo com uma aplicacao Go: FROM golang:1.23 AS builder WORKDIR /app COPY go.mod go.sum ./ RUN go mod download COPY . . RUN CGO_ENABLED=0 GOOS=linux go build -o /app/server FROM scratch COPY --from=builder /app/server /server CMD ["/server"] A imagem final comeca do zero (scratch). Nao tem shell, sistema operacional, nem ferramentas de build. So o binario compilado. Uma dica pratica: sempre nomeie seus stages com AS para facilitar a leitura. Use nomes como builder, test, ou dev. Isso ajuda a saber o que cada stage faz sem precisar contar linhas. That's all for now. Thanks for reading!
AI 资讯
How to Use FFmpeg with Swift (No Installation Required)
Originally published at ffmpeg-micro.com You need server-side video processing in your Swift app. Maybe you're building a Vapor backend that transcodes user uploads, a macOS utility that batch-converts media files, or a command-line tool that generates thumbnails. FFmpeg is the standard tool for the job, but getting it into a Swift project isn't as simple as adding a package dependency. Running FFmpeg from Swift with Process Swift's Foundation framework provides the Process class for running external commands. If FFmpeg is installed on the machine, you can shell out to it directly: import Foundation let process = Process () process . executableURL = URL ( fileURLWithPath : "/opt/homebrew/bin/ffmpeg" ) process . arguments = [ "-i" , "input.mp4" , "-c:v" , "libx264" , "-crf" , "23" , "-preset" , "medium" , "-c:a" , "aac" , "-b:a" , "128k" , "output.mp4" ] let pipe = Pipe () process . standardOutput = pipe process . standardError = pipe try process . run () process . waitUntilExit () let data = pipe . fileHandleForReading . readDataToEndOfFile () let output = String ( data : data , encoding : . utf8 ) ?? "" print ( output ) guard process . terminationStatus == 0 else { fatalError ( "FFmpeg failed with exit code \( process . terminationStatus ) " ) } This works on macOS and Linux. Install FFmpeg with brew install ffmpeg on macOS or apt-get install ffmpeg on Ubuntu, point executableURL at the binary, and you're running. But you own that FFmpeg install on every machine. On Linux servers, you're managing the binary across deploys. On macOS CI runners, you're adding Homebrew steps to your build pipeline. And on iOS, Process doesn't exist at all. Processing Video via Cloud API (No FFmpeg Install) Skip the local binary entirely. FFmpeg Micro exposes full FFmpeg capabilities through a REST API. Send a video URL, pick your settings, get processed video back. If you're familiar with how this works in Node.js or Kotlin , the pattern is identical. Here's the basic flow using URLSe