A generic, file-driven bootstrapper for workstations and service hosts, built in Rust.
Swiss installs packages, tools, repositories, files and shell integration from explicit YAML manifests. The binary ships only the engine: there is no embedded default environment. What gets installed and how your shells are configured is entirely defined by the manifest files you provide.
Three separable layers:
- Bootstrap engine — the
swissbinary: plans and executes manifests. - Manifests — declarative YAML files with includes, profiles and overlays (see docs/manifest.md).
- Shell integration modules — optional per-shell modules that generate or patch startup files for Nushell, zsh, bash and PowerShell.
git clone https://github.com/giovadroid/swiss.git
cd swiss
cargo install --path .One command bootstraps a fresh machine: it materializes a starter manifest, applies it and hooks the Swiss init into your shell:
swiss setup --manifest bootstrap.yaml --template dev-shellReview before touching anything, and keep everything fresh later:
swiss plan --manifest bootstrap.yaml # read-only
swiss doctor --manifest bootstrap.yaml # validate tools + manifest
swiss update # re-apply the registered manifestOr start from one of the examples:
swiss setup --manifest examples/workstation.yaml
swiss apply --manifest examples/bootstrap.yaml --profile service-host --yesswiss setup and swiss apply always require an explicit --manifest; Swiss never
applies anything implicitly.
| Command | Description |
|---|---|
swiss setup -m <path> [--template <name>] [--shell <name>] [-p <profile>...] [--yes] |
Full setup: apply the manifest, register it as active and hook the shell init (detected or --shell). --template writes a starter manifest (workstation, service-host, dev-shell) when the file does not exist |
swiss apply -m <path> [-p <profile>...] [--yes] [--dry-run] [--force-files] |
Bootstrap only: execute the plan, no shell registration |
swiss plan -m <path> [-p <profile>...] |
Show the execution plan without changing anything |
swiss update [--yes] |
Re-apply the manifest registered by setup/apply to update everything bootstrapped |
swiss init [--shell <name>] |
Shell startup hook: renders the live init script for the shell (detected from $SHELL, or --shell nushell|zsh|bash|pwsh) from the registered manifest and cached state |
swiss doctor [-m <path>] [-p <profile>...] |
Diagnose the installation and, optionally, validate a manifest |
swiss status |
Registered manifest, cached dependency state and fingerprint |
swiss clean-cache |
Delete the cache so the next apply re-runs everything |
setup vs apply: use apply to just bootstrap a host (CI, servers); use
setup to leave a machine fully integrated (manifest registered for
swiss update, shell init wired into your startup files).
includes: # compose multiple files (relative to this one)
- ./base.yaml
nushell: # optional: pin a version (omit for latest)
version: "0.101.0" # nu is just another shell Swiss can install
package_manager: # OS packages (apt / brew / scoop)
linux:
apt:
update_index: true
packages: [git, curl]
dependencies:
cargo: # cargo packages, installed via cargo-binstall
ripgrep:
zoxide:
args: ["--locked"]
alias: {cdi: "__zoxide_zi"}
customs: # git-sourced or scripted tools with install/update hooks
helix:
git: {repo: "https://github.com/helix-editor/helix", branch: master}
install: ["cargo install --path helix-term"]
files: # files/templates to write, with overwrite/append modes
- source: ./templates/starship.toml
dest: ~/.config/starship.toml
overwrite: false
shells: # optional shell integration, per shell
nushell: {mode: managed-loader}
zsh: {mode: snippet, target: ~/.zshrc, modules: [path, starship]}
shell_modules: # reusable named units shared by all shells
path:
env: {PATH: {prepend: ["~/.cargo/bin"]}}
starship:
init: {zsh: 'eval "$(starship init zsh)"'}
profiles: # named overlays selected with --profile
service-host:
package_manager:
linux: {apt: {packages: [docker.io]}}Full schema reference: docs/manifest.md.
Shell integration is optional and module-driven. Swiss never writes generated
init files during apply: it patches a single bounded block into your shell's
startup file that calls swiss init --shell <name> at every startup. That
command renders the init script live from the registered manifest plus the
dynamic state (cached aliases, SWISS_VERSION), so editing modules takes effect
on the next shell start with no re-apply. The block prepends ~/.cargo/bin to
PATH and is guarded with command -v swiss, so removing the binary never
breaks your shell. The rendered script wraps each module in bounded blocks:
# swiss begin: starship
eval "$(starship init zsh)"
# swiss end: starship
Modes:
managed-loader(Nushell): nu can'tevala dynamic string, so Swiss patches$nu.env-pathto regenerate~/.config/swiss/init.nufromswiss initat startup and$nu.config-pathto source it. Drop extra*.nufiles into~/.swiss/env/~/.swiss/confand they're appended to the script verbatim.snippet(zsh/bash): patches~/.zshrc/~/.bashrcwitheval "$(swiss init --shell <name>)".profile(PowerShell): same idea against$PROFILE, piped intoInvoke-Expression.print: nothing is patched; runswiss init --shell zsh(or bash/pwsh/nushell) and source/eval the output yourself.
Nushell is installed like any other tool (via cargo binstall) when a nushell
section is present or shells.nushell is enabled; zsh/bash/pwsh are installed
from the system package manager when missing. Swiss bootstraps Rust/cargo itself
when a manifest needs it, so it runs on a host with nothing pre-installed.
Service hosts can omit the shells section entirely: no shell files are touched.
swiss setup additionally registers the init hook for your current shell
(detected from $SHELL, override with --shell) when the manifest does not
mention it; a manifest that declares a shell — even disabled — is always the
source of truth.
-
Plain string commands in manifests run through the portable system shell (
shon Unix,powershellon Windows) — not Nushell, which may not be installed yet. Use the detailed form to pick a specific shell:commands: - run: ls | first # needs shell: nu shell: nu - run: ./configure && make install shell: sh requires_admin: true
-
swiss planis read-only;apply/setupprint the plan and ask for confirmation (skip with--yes). -
Output & logs:
apply/setupshow a live progress view — a spinner per step that resolves to✓or✗— and do not stop at the first failure: every step runs, failures are tallied in a final summary, and the command exits non-zero if any failed (so the rest of the plan still gets applied). A failed install never records its tool as present, so re-running retries only what's missing. Each run writes a full log (command output + diagnostics) to~/.config/swiss/logs/run-<id>.logfor later analysis. Pass-v/--verboseto also stream command output and debug logs to the terminal as they happen. -
The cache (
~/.config/swiss/.cache) records installed dependencies, aliases, the manifest fingerprint and the registered manifest used byswiss update; pinned cargo versions are skipped when already installed.swiss clean-cacheresets it. -
Secrets are never stored in manifests: use the
envsection to require or default environment variables.
cargo fmt --all --check
cargo clippy --all-targets --all-features --locked -- -D warnings
cargo test --all --lockedSwiss is released under the GNU General Public License v3.0.