Private Cargo build inputs
Publish locked private Git crates on a host that can read those repositories. The build worker receives only the immutable, checksummed input:
stado release catalog pin-input /path/to/product \
--name private-cargo-sources --source /path/to/product \
--revision HEAD --cargo --json
--cargo uses Cargo's real vendoring and includes only the Git packages named
by Cargo.lock. It verifies that Cargo manifests and lockfiles agree with the
requested committed revision, and checks them again after export. Scratch
files live in private-cargo-sources/ of the source checkout's build area under Stado's home (~/.stado/products/builds/<the checkout's path below the home>), never in the checkout, and are
removed when publication finishes or fails. No build is started.
The command stores the archive create-only by SHA-256, then atomically writes
inputs.private-cargo-sources in the product's .wisent-release.json.
Its receipt names the committed source, URI, digest, byte count and manifest.
Commit the changed manifest before submitting a release. Repeat publication
when a private package name, version or Git revision changes; changing only
the consumer's own version does not invalidate an existing input.
Without --cargo, pin-input publishes the requested Git tree as the build
reads it, archived under <name>/ and mounted extracted at
WISENT_INPUT_<NAME>_DIR: the commit's files, every submodule's files at the
commit the tree records for it, and every symlink inside the tree replaced by
the file or folder it names, so the worker's extraction holds regular files
only. A symlink that leaves the tree or names nothing is refused by name, and
so is a submodule whose recorded commit its checkout does not hold (git submodule update --init --recursive first). --path can select repository
paths in that mode; a path inside a submodule is refused with the
submodule's folder to name instead. Cargo mode refuses
--path, a name other than private-cargo-sources, uncommitted Cargo
declarations, an empty private-package set and failed vendoring or storage.
The Git refusal includes its output; a changed lockfile reports
Cargo.lock changed while exporting private sources; nothing was published.
An invalid manifest is refused before export.
--swiftpm publishes a Swift package's resolved dependencies for a desktop
release that builds offline with swift build --disable-automatic-resolution:
stado release catalog pin-input /path/to/desktop \
--name swiftpm-cache --source /path/to/desktop --revision HEAD --swiftpm --json
It runs swift package resolve --disable-automatic-resolution with a scratch
path of its own, so the resolution is exactly what the committed
Package.resolved pins, using this machine's Git credentials for private
packages. The archive's entries start at .build/ (checkouts/,
repositories/, artifacts/, workspace-state.json) and are written in name
order with cleared archive owners and times. Binary artifact paths in the
workspace state are relative to that cache, not to the publisher's temporary
directory. Archive bytes can differ between resolutions; the pinned SHA-256
identifies the actual published bytes. The input has mount <name>.tar.gz
and extract: false. The release recipe restores it before calling Swift:
stado product swift --package-path "$WISENT_SOURCE_DIR" \
--archive "$WISENT_INPUT_SWIFTPM_CACHE_DIR" restore
restore safely extracts into staging, checks the dependency set and actual Git
checkout revisions against the destination's Package.resolved, resolves each
binary artifact beneath the new cache, and then places the cache. It does not
compile or fetch dependencies. A repeated restore of the same archive checks
the checkouts and artifact files and returns reused: true; a different archive or an existing unowned
cache is refused instead of overwritten. Use a fresh release source directory
for a different input. Unrelated compiler outputs remain in place.
Missing files, unsafe archive members, revision mismatches, publisher-local
artifact paths and cleanup failures are reported with the failed path or
dependency. Older archives with absolute artifact paths must be republished.
Both the publisher and the release worker must support portable Swift inputs;
plain tar extraction is not a substitute for restoration.
A Package.swift or Package.resolved that differs from the revision, before
or after resolving, is refused with nothing was published; a failed resolve
reports SwiftPM's own output and exit status. A manifest sandbox refusal such
as sandbox_apply: Operation not permitted stops publication with that cause
and says what it means and where to go: the shell running pin-input is
itself sandboxed (an agent session, for one) and SwiftPM's manifest sandbox
cannot nest inside it; run the command from a shell outside the sandbox, or
have the Stado service run it, which is unsandboxed - Releases → Publish an
immutable build input in Stado Desktop, or POST /api/operator/run on the
service's API with X-Stado-Action: operator-command, args
["release","catalog","pin-input",…] and confirmation: RUN_MUTATION. The
service's process must be allowed to read the checkout and the source; on
macOS that is the folder access the system grants it, and a folder it is not
granted answers Operation not permitted from that route as the command
wrote it. Stado does not retry with SwiftPM's sandbox disabled or infer
permission to weaken isolation from an error message: a resolution made with
the sandbox off would publish bytes no sandbox had watched being made. CLI and
Desktop show the same refusal; no new input is pinned after a failed
resolution. --swiftpm cannot be combined with --cargo, --git-bundle or
--path.
In Stado Desktop, Releases → Publish an immutable build input exposes the same checkout, source revision, name and optional paths. Enable Export locked private Cargo dependencies for Cargo mode, or Export the Swift package's committed resolution for SwiftPM mode. The operation uses the selected Stado API host and returns the same receipt or refusal.
Stado Desktop's Products → Restore locked Swift release dependencies uses the same API-host operation, takes the release directory and archive, and shows the restoration receipt or refusal.
tests/release/inputs/swiftpm-input.mjs publishes to an isolated real Stado object
store, downloads and verifies the archive, restores it under a different path,
and runs SwiftPM against the restored dependencies without compiling. The real
package fixture must include an XCFramework and a dependency with a parent
commit so the journey checks relocated binary files, every locked checkout
revision and rejection of a changed pin. It also checks repeated restoration,
missing artifact and checkout refusals, conflicting export modes and absent
source/revision refusals. Its sandbox case requires a macOS qualification host
that actually refuses SwiftPM's nested manifest sandbox: the journey observes
that OS refusal first, then requires publication to preserve its cause and
leave the input manifest unchanged. The case runs on a host that exercises
that refusal:
STADO_BIN="$CANDIDATE_STADO" \
STADO_TEST_SWIFT_SOURCE="$SWIFT_PACKAGE_CHECKOUT" \
STADO_TEST_SWIFT_REVISION="$COMMITTED_REVISION" \
node tests/release/inputs/swiftpm-input.mjs
Reports under .build/swiftpm-input/run-*/ retain source and binary identities,
command output and exit statuses. A command is recorded as running before it
starts, so an interrupted run is not mistaken for a pass.
On a release worker, stado product cargo reads WISENT_SOURCE_DIR and
WISENT_INPUT_PRIVATE_CARGO_SOURCES_DIR:
stado product cargo --manifest-path "$WISENT_SOURCE_DIR/Cargo.toml" \
build --locked --release --bins
The shared executor checks the input's package set and source configuration against the lockfile, enables Cargo's directory-source replacement, preserves the lockfile and disables Git transport. Public registry dependencies retain their normal Cargo resolution. Build, check, test, run, metadata and stage share this handling. Outside a release worker they retain canonical-checkout resolution.
A missing input names WISENT_INPUT_PRIVATE_CARGO_SOURCES_DIR; a stale input
reports private Cargo sources do not match Cargo.lock with both sets.
An unsupported provenance schema or inconsistent source configuration is
refused before Cargo starts. Cargo checks the vendored file checksums.
Source manifests outside WISENT_SOURCE_DIR are refused. Arguments after
Cargo's -- still reach the test or program, not the source-config parser.
The publisher and worker must both support this interface; an older
publisher's unexpected argument '--cargo' is a refusal, not publication.
Real private-input qualification
From the Stado checkout, with a committed real Cargo product source:
STADO_BIN="$CANDIDATE" STADO_TEST_PRIVATE_SOURCE="$SOURCE" \
STADO_TEST_PRIVATE_REVISION="$REVISION" \
CARGO_HOME="$CARGO_HOME" RUSTUP_HOME="$RUSTUP_HOME" \
node tests/release/inputs/private-cargo-sources.mjs
The journey publishes into an isolated local object store, downloads and
hashes the archive, repeats publication, and runs real Cargo metadata against
the mounted sources with no private Git cache or transport. It also checks
contradictory flags, wrong names, missing inputs, stale package sets and
missing packages without changing the source lockfile or a refused manifest.
Reports below .build/private-cargo-sources/run-* retain source and binary
identity, commands, exit statuses, receipts and observations. This journey
does not compile a native binary or qualify a graphical screen. Native
product staging and graphical qualification remain separate real runs.
Source: this website