Once
Get started

Changelog

What's new in Once

Every user-facing change to Once, newest first. Subscribe to keep up.

RSS Atom
  1. Rule-neutral build and test reporting

    Once now keeps build and test reporting independent from target-kind names. Native compatibility commands select declared capabilities, tools, and resolver metadata, while test runners communicate through the normalized test-results contract. Toolchain names remain opaque in shared argument telemetry unless a workspace explicitly marks a value safe.

    Permalink →
  2. Cached fallback discovery and parallel package acquisition

    Xcode's fallback search for local Swift package products now reuses successful manifest dumps across graph loads. Manifest and source edits, added or removed files, tool changes and environment changes invalidate the saved answer. Invalid templates still produce a warning and are retried rather than saved as failures.

    Xcode and native Swift package workspaces acquire independent pinned Git repositories concurrently before reading their manifests. Acquisition shares Once's CPU and memory admission budget with build execution, including --cpu-limit and --memory-limit. Shared repository locking, offline reuse, pinned revisions and ordinary-fetch fallback remain unchanged.

    Permalink →
  3. "Immutable artifacts and explicit publication"

    Once can now export immutable, content-addressed build artifacts and publish them independently of its action cache. Artifacts can contain arbitrary data with optional versioned consumption contracts. Infrastructure owns distribution and how people use the content.

    Use once query artifacts, once artifact export, and once artifact publish, or their matching MCP tools. The built-in artifact target packages selected files; Apple application targets export bundles and native metadata. Local publication retains self-contained packages. The Tuist compatibility adapter uploads supported application payloads to Tuist.

    Publishing does not start compute. Tuist's current API does not support generic artifact storage, atomic variant sets or idempotent republishing. Read Artifacts for examples and the current boundaries.

    Permalink →
  4. "Native Xcode and Swift package compatibility fixes"

    Pinned Swift package checkouts now reuse commits already present locally and fall back to a full fetch when a Git server rejects shallow clients. The xcodebuild wrapper accepts custom configuration names when they match the Once seed, including Development.

    Native package lowering now honors linked libraries, frameworks, unsafe linker flags, and per-language warning policies. Manifest evaluation tracks a hash of the inherited environment so changes to manifest switches invalidate cached resolution without recording their values.

    Discovery skips invalid local package templates with a warning. Resolver failures make once query targets and its MCP equivalent fail rather than present an incomplete graph as successful. Literal directory exclusions prune descendants, and nested targets validate workspace runtime sources correctly.

    Swift package build-tool plugins now run during native builds, including source-built host tools, prebuild output discovery, generated resources, and commands that declare no outputs. Plugin failures fail the build. Plugin planning and execution run locally and bypass caching; native compilation remains cacheable. Xcode workspace fixtures exercise application builds, XCTest execution, environment changes, and regeneration after outputs are removed.

    Permalink →
  5. "Consistent package settings in custom Xcode configurations"

    Swift packages imported from Xcode now follow Swift Build's configuration mapping: names containing debug or development, ignoring case, select Debug; all other names select Release. Native project optimization settings and compilation conditions do not change that mapping.

    The automatic DEBUG definition and conditional Swift and C-family package settings now agree, including macro targets and their host dependencies. For example, Development enables debug-only package settings, while Staging selects release-only settings.

    Permalink →
  6. Reliable local package discovery and shared source reuse

    Xcode package discovery now follows explicit local package dependency paths before searching the workspace. Duplicate products or conflicting package identities report both candidate paths and explain how to select the intended dependency, instead of silently choosing a stale copy.

    Remote Swift packages reuse a shared Git repository across worktrees while keeping their source checkout workspace-local. Pinned commits remain available to other worktrees, and failed acquisitions do not publish partial checkouts. Servers that reject shallow fetches still work through an ordinary-fetch fallback.

    Warm graph loads reuse successful manifest dumps for referenced packages. Manifest edits, source changes, added or removed package files, tool changes and environment changes invalidate the saved metadata.

    Permalink →
  7. Swift package asset catalogs at runtime

    Once now compiles processed Swift package asset catalogs into Assets.car, so package images and colors resolve through Bundle.module at runtime. Multiple catalogs in one target are compiled together without colliding on their metadata files.

    Native package discovery also recognizes implicit Apple resources, while respecting explicit copy rules and excluded paths.

    Permalink →
  8. Infrastructure provider integration guide and contracts

    Infrastructure teams can now start with a top-to-bottom provider integration guide and use separate cache and execution references while implementing their service adapters.

    The references cover content and action-result formats, the existing REAPI cache transport, command preparation, resource limits, tenant isolation, and machine ownership and cleanup. They link to current implementations and regression tests and include acceptance checks for remote reuse and failures.

    The documentation distinguishes contracts available today from adapter work required for a new service. It does not introduce a new provider plug-in API or add support for another hosted service.

    Permalink →
  9. "Restore cached build outputs with less overhead"

    Remote cache hits reuse the blob availability established by their lookup, avoiding redundant requests. Output restoration submits known digest collections in bounded, deduplicated windows, packing small blobs into size-limited batch requests. Large blobs and servers without batching support keep streaming.

    Read-only staging pins raw blobs with a file handle and avoids synchronizing ephemeral decoded scratch files. Persistent cache contents and mutable output copies retain their existing durability guarantees, and all staged content is verified before it becomes readable.

    Batch responses are structurally bounded before decoding. Stalled responses time out, and cancelling one reader releases its resources without waiting for other readers in the same batch.

    Permalink →
  10. "Hosted test sharding through native Once commands"

    Create a hosted CI matrix with once query test-plan --shards and run each assignment with once test --shard-plan --shard-index. Once uses its existing native Tuist authentication and configuration, without launching the Tuist CLI.

    Hosted assignments preserve portable batch and fixture boundaries, including on fresh workers. Each partition submits one immutable report with native target/suite/name identities, ordered case retries, and explicit attempt and revision counters. Empty assignments run nothing; missing evidence, failed processes, and cancellation cannot produce a successful partition report.

    This requires a release containing the hosted commands. Once 0.71.0 supports portable plans only.

    Permalink →
  11. "Build runnable native macOS application bundles"

    macOS applications now use the native Contents layout. Executables live in Contents/MacOS, resources and compiled asset catalogs in Contents/Resources, and embedded frameworks in Contents/Frameworks. The framework runpath is set automatically, and generated property lists use macOS deployment metadata. iOS bundle paths are unchanged. Once rebuilds legacy flat macOS products without leaving obsolete files beside the new bundle contents.

    Native Xcode script phases receive the matching executable, property-list, and resource-directory settings. Scripts that previously assumed a flat macOS bundle must update their paths.

    The apple-application-macos starter is discoverable through the CLI and MCP. It includes a SwiftUI application and a bundled resource, ready for once run. The Apple guide also explains how to sign and notarize a private release copy without exposing release credentials or changing cached build products. Builds remain ad-hoc signed; Developer ID signing and provisioning are separate release responsibilities.

    Permalink →
  12. Correct cache invalidation for embedded Rust inputs

    Rust compilation now fingerprints every file declared by srcs, including non-Rust files embedded at compile time. Changing an embedded template, schema, or other declared data file rebuilds the crate and its affected consumers instead of restoring an outdated artifact from cache.

    Permalink →
  13. Cache Rust compilation by generated output content

    Rust compilation can now reuse cached artifacts after a generator rebuilds without changing its generated files or compiler directives, provided every other compiler input is unchanged.

    Build-script output directories also participate in downstream cache keys and input materialization. Changing a generated native library now relinks its consumers even when the Rust sources and build-script directives stay the same. Restoring a cached build includes those generated artifacts.

    Build scripts keep conservative package-wide input tracking. Generated files that contain changing timestamps still invalidate their consumers.

    Permalink →
  14. Select graph capabilities with experimental triggers

    Root once.toml triggers bind exact source refs to existing build, test, and run capabilities with explicit target configurations. An existing CI worker can execute those selections without maintaining another dependency graph. Inspection, validation, atomic editing, explicit activation, planning, and simulation are available through matching CLI and MCP interfaces.

    Activation uses a separately chosen committed definition, so a candidate that deletes or narrows its declaration does not silently remove its own selected work. Simulations require the exact clean checkout and use a dedicated local cache. CI still owns authenticated event delivery, worker isolation, resource ceilings, and required-check publication. This is not a native controller or an immutable replay facility.

    Read Event-Driven Automation for the service integration model and current support status. A Buildkite example adapter generates worker jobs from approved triggers through dynamic JSON pipelines. Tuist orchestration support is coming; no native service adapter is released yet.

    Permalink →
  15. "Improve native Apple build and test compatibility"

    Once handles more existing Xcode projects and Swift packages without changing their sources or delegating compilation to Xcode.

    Swift packages use single-worker batch compilation by default, avoiding repeated frontend startup and parsing for each source file. Whole-module optimization and explicit package compiler flag overrides remain supported.

    Swift package dependency resources are installed in the macOS test bundle's resources directory, and generated Bundle.module accessors find them there. Package C and C++ targets honor conditional header search paths.

    Frameworks that import XCTest receive its Swift overlay search path. Objective-C-only test bundles link through Clang, avoiding unnecessary Swift runtime compatibility libraries. Swift compilation and linking no longer load those libraries twice when a target requests all archive members.

    Embedded framework signing preserves versioned macOS layouts instead of creating a signature directory at the framework root. Resource bundles and help books also retain their complete directory trees. Synchronized groups preserve explicit resource folders and include web resources and scripting dictionaries. AppleScript sources compile into .scpt resources.

    Explicit macOS CODE_SIGNING_ALLOWED=NO configurations omit entitlement-bearing signing while retaining an ad-hoc bundle seal for local integrity checks. Capabilities that require authorized signing remain disabled in this mode.

    Xcode imports accept build phase scripts stored as arrays of lines, normalize relative script paths, and resolve package tool dependencies declared through target dependencies. Linked framework dependency matching also handles spaces in target names. Scripts receive transitive framework products and selected developer and toolchain paths.

    Swift builds honor native regular-expression and concurrency defaults. String catalogs compile into localized runtime resources and generate Swift symbols when the target enables them. Application-hosted macOS tests use Xcode's test runner without asking Xcode to build the products.

    Test execution uses each scheme's default plan instead of combining exclusions and arguments from its alternative plans.

    Test discovery preserves the enclosing suite across nested helper types, comments, strings, and local declarations, preventing duplicate test identities from corrupting the reported results. Source discovery does not imply execution: tests without an observed result remain unknown.

    Permalink →
  16. "Portable test batches for hosted CI sharding"

    Every once query test-plan --format json export is portable and replayable. Fresh CI workers can execute assigned opaque batches with once test --plan plan.json --batch <id> without transferring private discovery caches or reconstructing runner filters. Matching checkouts and tool versions can live at different paths. Changed discovery inputs or configuration fail validation; file fixtures and exact-result checks remain intact.

    Portable execution returns all assigned batch outcomes, including failed tests, with explicit completeness and success fields. External coordinators can attach a test-run group and partition index to authenticated native diagnostics and submit one consolidated test report per CI partition without duplicating each batch's cases.

    Permalink →
  17. Inspectable repeated-trial checks for builds and scripts

    Use once build --verify-reproducible <target> to compare two fresh builds, including their dependency closure. Script checks with once exec --verify-reproducible now snapshot the whole workspace and rerun needs dependencies and fingerprint commands inside each private trial. Both trials start from the same snapshot at the same private path, with separate, initially empty local Once caches. Once retains trial artifacts without replacing outputs in your original workspace.

    Execution status, declared artifacts, and captured logs are reported separately. Two identical failures never pass. Artifact differences fail the check; log-only differences are advisory when declared artifacts match. When there are no declared artifacts, captured logs determine the comparison.

    Every completed check returns an ID for once query verification <ID>, also available with --format json. Reports, per-trial results, decoded artifacts, captured logs, and local trial caches remain under .once/verifications/<ID>/. File-level differences include bounded text excerpts and first differing byte offsets when available, so you can inspect what changed instead of comparing digests alone. These files remain local and are not automatically published as project evidence.

    Coding harnesses can request the same checks with verify_reproducible: true on once_build_target and once_exec_script. The read-only once_query_verification tool retrieves a retained report without running commands again. Script --network deny now also applies to needs dependencies. Fingerprint commands that cannot enforce that policy fail closed instead of silently running online.

    A passing check means agreement under that artifact/log policy in two trials, not proof of reproducibility. Once does not perturb or control clocks, randomness, or thread scheduling. Host toolchains, host tool caches, absolute paths, network services, and external side effects are not isolated. The snapshot excludes Once runtime state and known declared script outputs, but other pre-existing generated files remain. Only start a check when repeated execution of the selected work and its dependencies is authorized.

    Permalink →
  18. Safer exit policies and cache reuse

    Commands now fail when they exit with 0 but their success_exit_codes list excludes it. Accepted nonzero exits continue to count as success, and failures retain their captured diagnostics. After upgrading, affected actions run again rather than reusing results recorded under the earlier behavior.

    Cache validation no longer trusts mutable timestamps where reliable change markers are unavailable. Content-based action caches continue to work, while unsafe metadata-only analysis and build shortcuts are disabled on those platforms. Rebuild checks on Windows may take longer as a result.

    Very deep build graphs no longer risk stack exhaustion while scheduling. Action plans validate dependencies in linear time, tolerate repeated dependency entries, and report worker panics instead of waiting indefinitely. SDK action keys keep their existing format while hashing inputs without buffering a second copy.

    Garbage collection remains a best-effort filesystem snapshot. Concurrent publication can cause a cache miss, which execution safely repairs by running the action again. Files disappearing during a collection pass no longer make the scan fail.

    Permalink →
  19. "Keep building through transient cache outages"

    Once can now continue executing scripts and build actions when the remote cache remains unavailable after bounded retries. Fresh results stay in the local cache and can be reused by later runs while the remote service recovers.

    If a deferred output becomes unavailable, a build can run its recorded producer again to reconstruct the exact expected bytes. Existing resource admission, repair limits, and digest verification still apply.

    Outages are logged and remain distinct from confirmed missing content. Cache reuse and restoration do not hide credential refusals, corruption, malformed cache records, or local filesystem failures. Explicit cache and SDK reads keep their existing error semantics.

    Permalink →
  20. "Reproducible test batches with stricter result validation"

    Once can rerun one semantic test batch without changing its file or fixture scope. Copy a batch ID from once query test-plan --target <target> and run once test <target> --test-batch <id>. The same selector is available through the test planning and execution MCP tools. Unknown or obsolete IDs fail before execution and suggest current batches.

    Automatic batches remain independent of worker count. Before scheduling a complete target scope, Once checks that batches do not overlap and cover every discovered unit. Exact results now reject duplicate or unrequested cases and require passing evidence in isolated batch outputs. A successful process exit alone cannot turn missing evidence or reported failures into a passing batch.

    Custom test adapters that previously reported extra cases for exact requests must honor the selected IDs. Keep setup diagnostics in runner metadata or artifacts, and give parameterized cases distinct discovered IDs. Use target-level batching when repeated startup or fixture setup outweighs parallelism.

    Permalink →
  21. Terminal status, live links, and smoother progress

    Once now reports whether a build, test, lint, or run command is working, finished, or failed to supporting terminals. Terminals can display that state without interpreting spinner characters or command output, and status reporting works without a reporting account or network connection.

    Live dashboard links are now clickable in interactive terminals and appear as soon as the reporting service returns the run's URL, so you can open the dashboard while work is still running. Links and log messages stay coordinated with the progress panel, and synchronized redraws reduce flicker on supporting terminals.

    Terminal integration uses portable protocols and is enabled automatically for interactive human output. Machine-output formats, redirected stderr, dumb terminals, and CI do not receive these controls. Disable them explicitly with --terminal-controls=never, independently of your color settings.

    See Terminal integration for quiet-mode behavior, cancellation, and output compatibility details.

    Permalink →
  22. Build iOS applications on Linux

    Once can cross-compile native Apple libraries, frameworks, applications, and executables on Linux using Swift and xtool's Darwin SDK. Compilation, linking, resource packaging, and ad-hoc signing remain declared Once actions, with the same caching and memory-budgeted scheduling as other builds.

    Linux iOS device builds default to ARM64, including on x86_64 hosts. Once uses the active Swift toolchain's Clang, the SDK's Mach-O linker and archiver, omarchy-apple-dev's installed resource compatibility tools, and rcodesign. You supply the Apple SDK through xtool; Once does not redistribute it.

    A Linux application starter is available through target-kind discovery:

    once edit materialize-example apple_application apple-application-linux
    once build Hello
    

    The output is ad-hoc signed, not provisioned for device installation or App Store submission. Simulator execution, Apple test runtimes, Xcode and Swift package workspace resolution, and Xcode app thinning still require macOS. See Build iOS Applications on Linux for setup, SDK repair after toolchain switches, and deployment boundaries.

    Permalink →
  23. Safer cached output restoration

    Once now stages cached files before replacing their workspace outputs. A concurrent reader keeps its complete original file instead of seeing it truncated during restoration. If a cached file cannot be read completely, the existing output stays intact.

    File permissions are applied before replacement, and restoration continues to stream through bounded buffers rather than loading whole artifacts into memory.

    Permalink →
  24. "More resilient cache discovery and consistent analysis limits"

    Once now reuses the last known remote cache endpoints when a discovery connection drops while receiving the response body. Without saved endpoints, the interruption becomes a cache miss instead of stopping the build. Malformed responses and authentication failures still surface as errors.

    Host discovery commands also keep their output limits separate in the analysis cache. A command previously run with a larger limit can no longer bypass a later caller's smaller standard-error limit.

    Permalink →
  25. More precise environment evidence

    Declared graph actions now record a separate fingerprint for each environment variable. When two builds have different action identities, you can compare their evidence to identify which variable changed without exposing its value.

    The additional evidence does not change action cache keys or invalidate existing cached results.

    Permalink →
  26. Stable managed tool resolution

    Once no longer treats developer-installed mise shims as concrete executables when resolving a workspace's declared tools. If workspace resolution is unavailable, it finds the underlying host tool instead of routing the action through a second mise installation.

    This keeps parallel builds from alternating between shim and host-tool search paths. Cached tool-path records that point at mise shims are rejected; valid records remain reusable.

    Permalink →
  27. Preserve generated Mix dependency resources

    Once now preserves compiler-generated priv and include files when building Mix dependencies, including native libraries produced by dependency compilers.

    Static package resources are combined with generated resources inside the compilation action instead of replacing them afterward. Both are captured in the cached application output, so cached builds retain the files needed to start dependent applications.

    Permalink →
  28. Keep release publication running

    New pushes no longer cancel release publication that is already in progress. New release runs wait instead, allowing artifact builds and package publication to finish while development continues.

    Permalink →
  29. Resilient Swift package analysis and host tool fallbacks

    Swift package analysis no longer fails when SwiftPM cannot fully describe a package, for example when a local binary artifact has not been produced yet or a declared test directory is missing from a downloaded dependency. Once keeps the manifest it already read and only asks SwiftPM for computed target paths when a target is not in its default location.

    When a workspace's declared tools fall back to the host toolchain, Once no longer remembers those host paths for later runs, so the next build tries workspace resolution again.

    Permalink →
  30. Clearer documentation and safer cache storage

    The documentation now offers a more direct path from your first cacheable script to a typed build graph. The getting-started example demonstrates both output restoration and input invalidation, and explains what you need to declare for cache reuse to be correct.

    A compact documentation home and previous/next links make the guides easier to follow on desktop and mobile. The target-kind reference now includes container composition, Dockerfile builds, Apple command-line tools, ShellSpec tests, and script adapters. Navigation also includes previously missing Elixir, Rust, and React Native reference pages.

    Cache storage cleans up temporary files after failed writes and streamed-blob installation. Host-tree hashing preserves literal Unix backslashes and rejects non-UTF-8 paths rather than assigning ambiguous identities. Existing metadata digest caches are refreshed automatically. Package-relative source paths now reject absolute paths before joining them to a package directory.

    Permalink →
  31. Create and connect a project with once connect

    once connect provisions a project with an infrastructure provider and writes the binding into your repository. With Tuist as the provider it signs you in when needed, creates the project, and records the account and project in the root once.toml, so a local project reaches a shared remote project in one command. The workflow is provider neutral: it reuses the same provider resolution as once auth login and writes the same configuration the rest of Once reads.

    Permalink →
  32. Container images without Docker or scripts

    Native container images are assembled by Once itself. oci_image can extend a base image, name several tags, and combine with other platforms through the new oci_index. oci_pull fetches a base by content digest, so it is immutable and cached. oci_push publishes an image or multi-platform index with the same digest Once built, and can sign it with cosign. oci_import brings in an image archive exported elsewhere. oci_load loads an archive into Docker or Podman. Layers can be gzip-compressed and carry symbolic links.

    Dockerfile images no longer need Python. Each instruction is a single Docker build step, and ARG and ENV values are resolved up front. A Dockerfile can use native executables and pinned images as named build contexts, instead of copying files into its context.

    Two correctness fixes reach beyond containers. Changing only a file's permission bits, such as making a script executable, now invalidates cached results built from it. Copied directory trees keep their empty directories.

    Permalink →
  33. More reliable Tuist cache uploads

    Once now retries temporary failures when checking or uploading blobs and publishing action results to the Tuist cache. Normal HTTP/2 connection retirement no longer immediately abandons an upload or cache entry.

    Retries are limited to three attempts. Interrupted uploads restart from the beginning with a fresh upload resource. Authentication failures, permission refusals, quota refusals, invalid requests, requests that exceed the request timeout, and local file errors still fail without retrying.

    Permalink →
  34. Dockerfile instructions as individual actions

    Once now translates existing Dockerfiles into individual image snapshot actions. Earlier instructions can reuse cached snapshots when later sources change, and cross-stage copies declare their dependencies explicitly. Container and remote BuildKit builders execute each instruction. Once also discovers a Dockerfile as an image target without once.toml. When Docker's built-in builder is the default, Once provisions a reusable container builder automatically.

    Dockerfile targets also expose advisory lint findings for inputs that may change outside the declared action contract. Findings include source locations and suggested repairs for agent review, without changing caching policy.

    Dockerfiles that use heredocs, ONBUILD, or a # syntax= frontend build as a single whole-file BuildKit action automatically, and once lint reports why. Cacheable images now require base images pinned by digest, so a moving tag cannot serve a stale cached image.

    Permalink →
  35. Portable Dockerfile layer caches

    Dockerfile image targets can export intermediate BuildKit layers as declared outputs and reuse them through target dependencies. Once can store and restore those layers with image archives, including Docker archives built with a container or remote builder.

    Registry cache imports and exports let independent builders share layers. Select a remote BuildKit builder to run Dockerfile instructions away from the local host while Once tracks the context inputs and exported artifacts.

    Permalink →
  36. Native Apple test outcomes in Once reports

    Apple test bundles run by the XCTest host now translate completed test cases from the native runner output into Once results, including their outcome and duration. This also covers Swift Testing cases in a bundle that uses both runners. Source discovery still records cases that the runner did not reach, but leaves their outcome unknown instead of treating a passing process as proof that every discovered case passed.

    Permalink →
  37. Read the Xcode 27.2 project.xcproj JSON manifest

    Once now recognizes the JSON-based project.xcproj manifest that Xcode 27.2 writes alongside, or in place of, the classic OpenStep project.pbxproj. A converted project resolves through the same xcode_workspace target kind as before, so target names, discovery, and downstream build actions stay unchanged. Projects that ship both manifests prefer the JSON form; projects that ship only project.pbxproj keep working exactly as they did.

    The reader handles the shape choices that Xcode 27.2 uses in real projects, including grouped and file-system synchronized folders with per-target inclusion and exclusion lists, per-configuration setting suffixes such as ENABLE_TESTABILITY[config=Debug], per-configuration xcconfig links, string and object build phases, remote and local Swift package references with their product members, and short product-type spellings such as framework or bundle.unit-test.

    Two long-standing gaps that surfaced while validating the reader against Alamofire, Kingfisher, RxSwift, MovieSwiftUI, and Ice Cubes are fixed in the same release and benefit the classic manifest reader as well. Frameworks whose hosted tests use @testable import now compile the dependency for testing, so test bundles resolve the module they import. Frameworks that themselves import XCTest, such as RxTest, are given the platform's Developer framework and library search paths so they compile. Build settings that nest one variable inside another, such as Alamofire's $(MACOSX_DEPLOYMENT_TARGET_XCODE_$(XCODE_VERSION_MAJOR)), now resolve to a concrete deployment target using the current Xcode's version.

    Permalink →
  38. Explicit modules for native Apple builds

    Once can now use the Swift compiler's dependency scan to build imported Swift and Clang modules as separate cacheable actions. Enable explicit_modules on an Apple target or a native Xcode or Swift package workspace. Native Xcode projects also honor SWIFT_ENABLE_EXPLICIT_MODULES = YES.

    Shared module work reuses the action cache, and independent declared actions can run concurrently within the build's memory budget. Module identity includes compiler and development-kit contents. Existing builds keep their implicit-module behavior unless explicit modules are selected.

    An optional dependency_check = "error" rejects source imports of workspace modules that are not declared dependencies. Errors include the target and a suggested repair. Xcode's inferred import edges do not satisfy this check.

    The module authoring contract now includes deferred action planning and host directory digests. These are ecosystem-neutral primitives that other target kinds can use. This release also fixes signing for standalone Apple executables. Parent-relative dependency references now resolve correctly in nested Apple packages.

    See Apple builds for configuration and current limits.

    Permalink →
  39. Correct local and multi-platform Apple dependency graphs

    Once now follows transitive local Swift package dependencies without requiring a lockfile for an entirely local graph. Remote dependencies introduced by local packages still use the root package's dependency resolution.

    Xcode projects with both macOS and iPhone targets now get distinct dependency graphs for each destination, including package targets and binary frameworks. Manifest parsing is shared across destinations and local product discovery.

    Package product dependencies retain every module in the product, even when a module shares the product's name. Native application attributes and system library dependencies also remain consistent with their graph contracts.

    These improvements came from exercising Vapor and NetNewsWire. Small, offline acceptance fixtures preserve their graph patterns and check dependency edges, source membership, settings, selective builds, cache restoration, and changes to transitive inputs without compiling the full applications.

    Read about Swift packages and Xcode projects.

    Permalink →
  40. Run native Xcode script phases with reconciled build settings

    Once now retains Xcode script phases without source or archive outputs, including validation and product-processing scripts. Preparation runs before compilation, resource generators can run between linking and packaging, and product scripts run after assembly and before final signing. Later scripts consume the bundle produced by earlier scripts, including undeclared resources created by always-run phases. Deleted resources stay deleted, and restoring a captured bundle no longer overwrites newer outputs inside it.

    Scripts receive native build settings mapped to Once's workspace, product, and intermediate directories, including input and output file-list variables. Fully declared scripts can reuse cached results. Always-run scripts and scripts with untracked inputs bypass the cache while allowing individual compilation actions to remain cached. Installation-only phases remain excluded from ordinary builds.

    Native shell phases execute in the workspace so absolute build-setting paths and in-place edits address the same products. Caching relies on complete input and output declarations, not filesystem isolation.

    Permalink →
  41. Test results that follow the run

    Swift Testing bundles now report every test and what actually became of it. The testing library keeps a record of the run, and Once turns that record into its normalized results instead of listing what the sources declare and crediting each one with the run's exit status. A test that was filtered out, skipped, or never reached is reported as such rather than as a pass, and an issue a test marks as known no longer counts as a failure.

    Where results still have to come from the XCTest host, that host reports the run's outcome rather than each test's. Once lists the cases it finds so a shard can address them, but lists them without a verdict, so a green run never carries verdicts nothing produced.

    Test bundles that depend on a Swift macro can now import it. Code behind canImport of a macro module used to compile away, which quietly emptied the test targets that exercise macro implementations. Such a bundle builds for the host along with its dependencies, matching how a macro itself is built.

    Permalink →
  42. Preserve Swift dependency traits

    Native Swift package builds now preserve traits requested by dependencies. Once combines requests across the package graph and expands traits that enable other traits before compiling libraries and macros. This fixes missing types when a dependency exposes functionality behind an opt-in trait.

    Explicit trait lists, disabled defaults, and conditional trait requests follow the package declarations. The same resolved traits control compiler settings and optional target dependencies.

    Permalink →
  43. Builds with a Swift toolchain outside Xcode

    A Swift toolchain installed beside Xcode contributes the compiler, but the linker and the software development kit still come from Xcode. Apple builds now resolve those separately and pass the linker's location to every compile and link, so a package that needs a newer compiler than the one Xcode ships builds without falling back to whatever the surrounding shell happened to expose.

    Swift Testing follows the same rule. A macro plugin only matches the testing library from its own toolchain, and the two ship on different schedules, so test targets now compile and link against the copy the selected compiler provides when it has one, and against the version Xcode publishes otherwise. Test bundles that previously failed to expand @Test and @Suite build again.

    Swift package libraries are also force-loaded into what depends on them, the way Swift Package Manager links every object file a target produced. A source file whose only contribution is a protocol conformance is no longer dropped, so conformances declared in an extension are found at runtime.

    Permalink →
  44. Explainable input fingerprints

    Evidence records now explain why a build's inputs changed, not only that they changed. Alongside the input digest it already tracked, Once records a versioned input fingerprint that breaks the digest down into labeled components for sources, generated inputs, dependencies, module code, toolchains, command shape, and environment configuration.

    Each component carries a category, a stable label, and its own digest, so two runs can be compared to see exactly which part of the input moved. Targets that run several actions aggregate their per-action fingerprints into a single target-level view.

    The fingerprint is available from both command and agent evidence queries, and older evidence stays readable because the field is optional. Raw command arguments, toolchain identities, and environment values are represented only by their digests and are never stored in plain text, so the fingerprint stays safe to share.

    Permalink →
  45. Faster native project discovery

    Once now discovers native Cargo, Mix, and future ecosystem projects with one shared filesystem pass. Workspace include and exclude patterns prune unrelated directories before Once enters them, reducing startup work in large repositories without changing the targets that discovery produces.

    Discovery also keeps its own memory usage predictable. Project kinds that own nested packages retain only their shallowest matching roots, while kinds that recognize every nested project have a 16 mebibyte retained-result limit. Once stops with guidance for narrowing the workspace if that limit is reached, instead of allowing the number of matching directories to cause unbounded memory growth.

    Permalink →
  46. Infrastructure favicons in the documentation sidebar

    The documentation sidebar now shows the Microsandbox, E2B, Daytona, and Tuist favicons next to their infrastructure guides, matching the visual treatment used for supported ecosystems.

    Permalink →
  47. Near-instant unchanged builds

    Once now returns almost immediately when nothing relevant has changed. A build that already matches the cache no longer revalidates the whole graph on every invocation. Instead, Once records what changed since the last successful build and reuses the previous result when the sources, declared outputs, environment, and tools it observed are all unchanged.

    Input checking is also cheaper. Once validates unchanged files by their metadata rather than rereading their contents, so a repeat build no longer pays to rehash the entire dependency tree. When a cached result is reused, only the outputs that actually differ are restored. Together these reduce both the time and the memory an unchanged or lightly changed build needs.

    Correctness is preserved conservatively: any change to a watched source or output, a relevant environment variable, or an observed tool on disk falls back to a full validation before the fast path is trusted again.

    Permalink →
  48. Native Cargo project graphs

    Once can now read an existing Cargo.toml and turn it into a typed, cacheable build graph. Rust developers can build, run, and test a workspace without first translating it into once.toml.

    Native discovery understands Cargo workspaces and the locked dependency closure from Cargo.lock, including registry, Git, and path packages. Each workspace member and locked dependency compiles as its own action, so unchanged work can be restored independently from a local or remote cache. Procedural macros and build scripts are modeled as their own targets so they rebuild only when their own inputs change.

    Once keeps Cargo.toml and Cargo.lock authoritative and uses the Rust toolchain selected by the project's configuration. Test targets report their results through the same normalized shape as the other ecosystems, so scheduled runs, change-scoped selection, and summaries work the same way.

    Start by running once query targets, then use once build, once run, and once test with the target identifiers Once reports.

    Permalink →
  49. Bounded memory scheduling for local actions

    Once now schedules concurrent local actions against a memory budget. The default is two thirds of the memory visible to the host or container, while the new global --memory-limit option lets constrained machines choose a smaller budget.

    Builds, lint targets, run targets, literal commands, scripts, and test workers share the same limit. Actions without a declared estimate receive a conservative default, and oversized actions run alone instead of waiting forever.

    Large command streams, file outputs, directory outputs, and cache uploads or downloads now move through bounded buffers and temporary files instead of requiring complete artifacts in memory.

    Permalink →
  50. Native Mix project graphs

    Once can now read an existing mix.exs and turn it into a typed, cacheable build graph. Elixir developers can build an application, run formatting and dependency checks, execute tests and Mix tasks, and assemble releases without first translating the project into once.toml.

    Native discovery understands locked Hex, Git, path, Mix, and Rebar 3 dependencies. Each package compiles as its own action, so unchanged dependency work can be restored independently from a local or remote cache. The graph also keeps development, test, and production environments separate.

    Once uses the Elixir and Erlang versions selected by the project's toolchain configuration while keeping mix.exs and mix.lock authoritative. Umbrella applications and nested Mix projects receive package-qualified targets, and structured diagnostics explain unsupported dependency build managers or missing materialized sources.

    Start by running once query targets, then use once build, once run, and once test with the target identifiers Once reports.

    Permalink →
  51. Device-specific Apple app size analysis

    Once can now produce ad-hoc signed app archives for one Apple device model at a time with apple_thinned_package. Keeping each model in its own target makes the variants independently cacheable and ready for size-analysis services such as Sentry without the noise of a universal build.

    The target validates device builds, uses Xcode's thinning tool, re-signs embedded bundles, and writes a deterministic archive plus a stable manifest. Existing apple_application bundles now include the supported-platform metadata that Xcode requires for thinning.

    Permalink →
  52. "New blog post: Automation needs a git"

    The first post on the Once blog is live. "Automation needs a git" is Pedro's account of why Once exists: the search for a shared substrate for automation, the incremental shell-based API for decorating existing scripts, and native support for ecosystems starting with Rust. Read it under the blog.

    Permalink →
  53. A blog for ideas from the Once team

    The Once website now has a blog for longer-form notes about build graphs, reusable automation, and the ideas shaping the project.

    Posts include their authors and Gravatar portraits. The list is paginated, each post has its own generated Open Graph image with author portraits, and readers can subscribe through Really Simple Syndication or Atom feeds. Markdown is compiled with MDEx, with highlighted code windows built from Noora components.

    Permalink →
  54. Cacheable linting with normalized findings

    Once can now run lint targets through the same graph, cache, evidence, and remote-execution model used by builds and tests. The first integrations cover Ruff, golangci-lint, ESLint, RuboCop, Credo, detekt, and SwiftLint.

    once lint keeps each analyzer's native configuration authoritative while presenting one finding model and failure policy to humans, automation, and coding agents. Project modules can add another analyzer through the documented lint provider and portable report contract.

    The bundled ESLint starter includes its required flat configuration, Credo limits analysis to declared sources, and RuboCop prepares Ruby for portable report conversion. Invalid custom lint providers now return structured repair diagnostics before their actions run.

    Permalink →
  55. Documentation now lives in the Once app

    The documentation moved from a separate static site into the Once web application, served under /docs. It is rendered natively with the Noora design system, so the docs and the marketing site now share one look and feel.

    This unlocks upcoming interactive features like a package registry and live content, and lets the docs run on the same infrastructure as the rest of Once.

    Permalink →
  56. Refreshed marketing site and a changelog

    The Once home page was rebuilt around the idea of build once, reuse everywhere, with a new hero, tabbed build examples for iOS, Android, Rust, and Zig, and the list of natively supported languages and ecosystems.

    This changelog is new too. Every user-facing change to Once will show up here, and you can subscribe over RSS or Atom.

    Permalink →