From d438aaf9ec61d121c4af2360b1127054a557875e Mon Sep 17 00:00:00 2001 From: Colin Walters Date: Thu, 4 Sep 2025 18:11:14 -0400 Subject: [PATCH] Switch to hand-written man pages with auto option sync See the updates to `Justfile` for how to use this. Closes: #1428 Assisted-By: Claude Code (opus + sonnet) Signed-off-by: Colin Walters --- .github/workflows/ci.yml | 2 +- .github/workflows/scheduled-release.yml | 2 +- Cargo.lock | 2 + Justfile | 11 + Makefile | 36 +- contrib/packaging/bootc.spec | 3 + crates/lib/src/cli.rs | 107 +--- crates/lib/src/cli_json.rs | 127 ++++ crates/lib/src/docgen.rs | 55 -- crates/lib/src/lib.rs | 2 +- crates/xtask/Cargo.toml | 2 + crates/xtask/src/man.rs | 597 ++++++++++++++++++ crates/xtask/src/xtask.rs | 121 +--- docs/src/man/bootc-config.5.md | 57 ++ docs/src/man/bootc-container-lint.8.md | 60 ++ docs/src/man/bootc-container-lint.md | 56 -- docs/src/man/bootc-container.8.md | 29 + docs/src/man/bootc-container.md | 33 - docs/src/man/bootc-edit.8.md | 42 ++ docs/src/man/bootc-edit.md | 39 -- .../bootc-fetch-apply-updates.service.5.md} | 6 +- .../bootc-install-config.5.md} | 4 + docs/src/man/bootc-install-config.md | 1 - ...d => bootc-install-ensure-completion.8.md} | 16 +- docs/src/man/bootc-install-finalize.8.md | 35 + docs/src/man/bootc-install-finalize.md | 27 - .../bootc-install-print-configuration.8.md | 28 + .../man/bootc-install-print-configuration.md | 30 - docs/src/man/bootc-install-to-disk.8.md | 171 +++++ docs/src/man/bootc-install-to-disk.md | 163 ----- .../man/bootc-install-to-existing-root.8.md | 134 ++++ .../src/man/bootc-install-to-existing-root.md | 155 ----- docs/src/man/bootc-install-to-filesystem.8.md | 142 +++++ docs/src/man/bootc-install-to-filesystem.md | 176 ------ docs/src/man/bootc-install.8.md | 51 ++ docs/src/man/bootc-install.md | 74 --- docs/src/man/bootc-rollback.8.md | 75 +++ docs/src/man/bootc-rollback.md | 75 --- .../bootc-status-updated.path.5.md} | 4 + .../bootc-status-updated.target.5.md} | 4 + docs/src/man/bootc-status.8.md | 85 +++ docs/src/man/bootc-status.md | 66 -- docs/src/man/bootc-switch.8.md | 112 ++++ docs/src/man/bootc-switch.md | 85 --- docs/src/man/bootc-upgrade.8.md | 98 +++ docs/src/man/bootc-upgrade.md | 72 --- docs/src/man/bootc-usr-overlay.8.md | 40 ++ docs/src/man/bootc-usr-overlay.md | 42 -- docs/src/man/bootc.8.md | 42 ++ docs/src/man/bootc.md | 75 --- 50 files changed, 2051 insertions(+), 1420 deletions(-) create mode 100644 crates/lib/src/cli_json.rs delete mode 100644 crates/lib/src/docgen.rs create mode 100644 crates/xtask/src/man.rs create mode 100644 docs/src/man/bootc-config.5.md create mode 100644 docs/src/man/bootc-container-lint.8.md delete mode 100644 docs/src/man/bootc-container-lint.md create mode 100644 docs/src/man/bootc-container.8.md delete mode 100644 docs/src/man/bootc-container.md create mode 100644 docs/src/man/bootc-edit.8.md delete mode 100644 docs/src/man/bootc-edit.md rename docs/src/{man-md/bootc-fetch-apply-updates.service.md => man/bootc-fetch-apply-updates.service.5.md} (93%) rename docs/src/{man-md/bootc-install-config.md => man/bootc-install-config.5.md} (97%) delete mode 100644 docs/src/man/bootc-install-config.md rename docs/src/man/{bootc-install-ensure-completion.md => bootc-install-ensure-completion.8.md} (67%) create mode 100644 docs/src/man/bootc-install-finalize.8.md delete mode 100644 docs/src/man/bootc-install-finalize.md create mode 100644 docs/src/man/bootc-install-print-configuration.8.md delete mode 100644 docs/src/man/bootc-install-print-configuration.md create mode 100644 docs/src/man/bootc-install-to-disk.8.md delete mode 100644 docs/src/man/bootc-install-to-disk.md create mode 100644 docs/src/man/bootc-install-to-existing-root.8.md delete mode 100644 docs/src/man/bootc-install-to-existing-root.md create mode 100644 docs/src/man/bootc-install-to-filesystem.8.md delete mode 100644 docs/src/man/bootc-install-to-filesystem.md create mode 100644 docs/src/man/bootc-install.8.md delete mode 100644 docs/src/man/bootc-install.md create mode 100644 docs/src/man/bootc-rollback.8.md delete mode 100644 docs/src/man/bootc-rollback.md rename docs/src/{man-md/bootc-status-updated.path.md => man/bootc-status-updated.path.5.md} (91%) rename docs/src/{man-md/bootc-status-updated.target.md => man/bootc-status-updated.target.5.md} (92%) create mode 100644 docs/src/man/bootc-status.8.md delete mode 100644 docs/src/man/bootc-status.md create mode 100644 docs/src/man/bootc-switch.8.md delete mode 100644 docs/src/man/bootc-switch.md create mode 100644 docs/src/man/bootc-upgrade.8.md delete mode 100644 docs/src/man/bootc-upgrade.md create mode 100644 docs/src/man/bootc-usr-overlay.8.md delete mode 100644 docs/src/man/bootc-usr-overlay.md create mode 100644 docs/src/man/bootc.8.md delete mode 100644 docs/src/man/bootc.md diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index 74df59abd..2f3ece383 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -39,7 +39,7 @@ jobs: - name: Run tests run: cargo test -- --nocapture --quiet - name: Manpage generation - run: mkdir -p target/man && cargo run --features=docgen -- man --directory target/man + run: cargo xtask update-generated - name: Clippy (gate on correctness and suspicous) run: make validate-rust fedora-container-tests: diff --git a/.github/workflows/scheduled-release.yml b/.github/workflows/scheduled-release.yml index 15025ce55..c7f43f1c2 100644 --- a/.github/workflows/scheduled-release.yml +++ b/.github/workflows/scheduled-release.yml @@ -85,7 +85,7 @@ jobs: env: INPUT_VERSION: ${{ github.event.inputs.version }} run: | - dnf -y install pandoc + dnf -y install go-md2man cargo install cargo-edit # If version is provided via workflow dispatch, validate and use it diff --git a/Cargo.lock b/Cargo.lock index 0609f16d7..c4e61bc73 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -3394,6 +3394,8 @@ dependencies = [ "chrono", "fn-error-context", "mandown", + "serde", + "serde_json", "tar", "tempfile", "toml 0.9.5", diff --git a/Justfile b/Justfile index 756866232..133de4d1f 100644 --- a/Justfile +++ b/Justfile @@ -16,3 +16,14 @@ run-container-external-tests: unittest *ARGS: podman build --jobs=4 --target units -t localhost/bootc-units --build-arg=unitargs={{ARGS}} . + +# Update all generated files (man pages and JSON schemas) +# +# This is the unified command that: +# - Auto-discovers new CLI commands and creates man page templates +# - Syncs CLI options from Rust code to existing man page templates +# - Updates JSON schema files +# +# Use this after adding, removing, or modifying CLI options or schemas. +update-generated: + cargo run -p xtask update-generated diff --git a/Makefile b/Makefile index 8b308a484..5a8fedd8e 100644 --- a/Makefile +++ b/Makefile @@ -10,9 +10,28 @@ TAR_REPRODUCIBLE = tar --mtime="@${SOURCE_DATE_EPOCH}" --sort=name --owner=0 --g # (Note we should also make installation of the units conditional on the rhsm feature) CARGO_FEATURES ?= $(shell . /usr/lib/os-release; if echo "$$ID_LIKE" |grep -qF rhel; then echo rhsm; fi) -all: +all: bin manpages + +bin: cargo build --release --features "$(CARGO_FEATURES)" +# Generate man pages from markdown sources +MAN5_SOURCES := $(wildcard docs/src/man/*.5.md) +MAN8_SOURCES := $(wildcard docs/src/man/*.8.md) +TARGETMAN := target/man +MAN5_TARGETS := $(patsubst docs/src/man/%.5.md,$(TARGETMAN)/%.5,$(MAN5_SOURCES)) +MAN8_TARGETS := $(patsubst docs/src/man/%.8.md,$(TARGETMAN)/%.8,$(MAN8_SOURCES)) + +$(TARGETMAN)/%.5: docs/src/man/%.5.md + @mkdir -p $(TARGETMAN) + go-md2man -in $< -out $@ + +$(TARGETMAN)/%.8: docs/src/man/%.8.md + @mkdir -p $(TARGETMAN) + go-md2man -in $< -out $@ + +manpages: $(MAN5_TARGETS) $(MAN8_TARGETS) + STORAGE_RELATIVE_PATH ?= $(shell realpath -m -s --relative-to="$(prefix)/lib/bootc/storage" /sysroot/ostree/bootc/storage) install: install -D -m 0755 -t $(DESTDIR)$(prefix)/bin target/release/bootc @@ -22,15 +41,12 @@ install: ln -s "$(STORAGE_RELATIVE_PATH)" "$(DESTDIR)$(prefix)/lib/bootc/storage" install -D -m 0755 crates/cli/bootc-generator-stub $(DESTDIR)$(prefix)/lib/systemd/system-generators/bootc-systemd-generator install -d $(DESTDIR)$(prefix)/lib/bootc/install - # Support installing pre-generated man pages shipped in source tarball, to avoid - # a dependency on pandoc downstream. But in local builds these end up in target/man, - # so we honor that too. - for d in man target/man; do \ - if test -d $$d; then \ - install -D -m 0644 -t $(DESTDIR)$(prefix)/share/man/man5 $$d/*.5; \ - install -D -m 0644 -t $(DESTDIR)$(prefix)/share/man/man8 $$d/*.8; \ - fi; \ - done + if [ -n "$(MAN5_TARGETS)" ]; then \ + install -D -m 0644 -t $(DESTDIR)$(prefix)/share/man/man5 $(MAN5_TARGETS); \ + fi + if [ -n "$(MAN8_TARGETS)" ]; then \ + install -D -m 0644 -t $(DESTDIR)$(prefix)/share/man/man8 $(MAN8_TARGETS); \ + fi install -D -m 0644 -t $(DESTDIR)/$(prefix)/lib/systemd/system systemd/*.service systemd/*.timer systemd/*.path systemd/*.target install -d -m 0755 $(DESTDIR)/$(prefix)/lib/systemd/system/multi-user.target.wants ln -s ../bootc-status-updated.path $(DESTDIR)/$(prefix)/lib/systemd/system/multi-user.target.wants/bootc-status-updated.path diff --git a/contrib/packaging/bootc.spec b/contrib/packaging/bootc.spec index dfc1d5b91..50bbd0c81 100644 --- a/contrib/packaging/bootc.spec +++ b/contrib/packaging/bootc.spec @@ -36,6 +36,7 @@ BuildRequires: libzstd-devel BuildRequires: make BuildRequires: ostree-devel BuildRequires: openssl-devel +BuildRequires: go-md2man %if 0%{?rhel} BuildRequires: rust-toolset %else @@ -107,6 +108,8 @@ export SYSTEM_REINSTALL_BOOTC_INSTALL_PODMAN_PATH=%{system_reinstall_bootc_insta %cargo_build %cargo_args %endif +make manpages + %cargo_vendor_manifest # https://pagure.io/fedora-rust/rust-packaging/issue/33 sed -i -e '/https:\/\//d' cargo-vendor.txt diff --git a/crates/lib/src/cli.rs b/crates/lib/src/cli.rs index 12b576f05..93be94c2c 100644 --- a/crates/lib/src/cli.rs +++ b/crates/lib/src/cli.rs @@ -39,10 +39,9 @@ use crate::utils::sigpolicy_from_opt; /// Shared progress options #[derive(Debug, Parser, PartialEq, Eq)] pub(crate) struct ProgressOptions { - /// File descriptor number which must refer to an open pipe (anonymous or named). + /// File descriptor number which must refer to an open pipe. /// - /// Interactive progress will be written to this file descriptor as "JSON lines" - /// format, where each value is separated by a newline. + /// Progress is written as JSON lines to this file descriptor. #[clap(long, hide = true)] pub(crate) progress_fd: Option, } @@ -69,23 +68,19 @@ pub(crate) struct UpgradeOpts { /// Check if an update is available without applying it. /// - /// This only downloads an updated manifest and image configuration (i.e. typically kilobyte-sized metadata) - /// as opposed to the image layers. + /// This only downloads updated metadata, not the full image layers. #[clap(long, conflicts_with = "apply")] pub(crate) check: bool, /// Restart or reboot into the new target image. /// - /// Currently, this option always reboots. In the future this command - /// will detect the case where no kernel changes are queued, and perform - /// a userspace-only restart. + /// Currently, this always reboots. Future versions may support userspace-only restart. #[clap(long, conflicts_with = "check")] pub(crate) apply: bool, /// Configure soft reboot behavior. /// - /// 'required' will fail if soft reboot is not available. - /// 'auto' will use soft reboot if available, otherwise fall back to regular reboot. + /// 'required' fails if soft reboot unavailable, 'auto' falls back to regular reboot. #[clap(long = "soft-reboot", conflicts_with = "check")] pub(crate) soft_reboot: Option, @@ -102,16 +97,13 @@ pub(crate) struct SwitchOpts { /// Restart or reboot into the new target image. /// - /// Currently, this option always reboots. In the future this command - /// will detect the case where no kernel changes are queued, and perform - /// a userspace-only restart. + /// Currently, this always reboots. Future versions may support userspace-only restart. #[clap(long)] pub(crate) apply: bool, /// Configure soft reboot behavior. /// - /// 'required' will fail if soft reboot is not available. - /// 'auto' will use soft reboot if available, otherwise fall back to regular reboot. + /// 'required' fails if soft reboot unavailable, 'auto' falls back to regular reboot. #[clap(long = "soft-reboot")] pub(crate) soft_reboot: Option, @@ -161,8 +153,7 @@ pub(crate) struct RollbackOpts { /// Configure soft reboot behavior. /// - /// 'required' will fail if soft reboot is not available. - /// 'auto' will use soft reboot if available, otherwise fall back to regular reboot. + /// 'required' fails if soft reboot unavailable, 'auto' falls back to regular reboot. #[clap(long = "soft-reboot")] pub(crate) soft_reboot: Option, } @@ -279,14 +270,6 @@ pub(crate) enum InstallOpts { PrintConfiguration, } -/// Options for man page generation -#[derive(Debug, Parser, PartialEq, Eq)] -pub(crate) struct ManOpts { - #[clap(long)] - /// Output to this directory - pub(crate) directory: Utf8PathBuf, -} - /// Subcommands which can be executed as part of a container build. #[derive(Debug, clap::Subcommand, PartialEq, Eq)] pub(crate) enum ContainerOpts { @@ -540,6 +523,9 @@ pub(crate) enum InternalsOpts { #[clap(long)] merge: bool, }, + #[cfg(feature = "docgen")] + /// Dump CLI structure as JSON for documentation generation + DumpCliJson, } #[derive(Debug, clap::Subcommand, PartialEq, Eq)] @@ -625,70 +611,26 @@ pub(crate) enum Opt { /// /// Only changes to the `spec` section are honored. Edit(EditOpts), - /// Display status - /// - /// If standard output is a terminal, this will output a description of the bootc system state. - /// If standard output is not a terminal, output a YAML-formatted object using a schema - /// intended to match a Kubernetes resource that describes the state of the booted system. - /// - /// ## Parsing output via programs - /// - /// Either the default YAML format or `--format=json` can be used. Do not attempt to - /// explicitly parse the output of `--format=humanreadable` as it will very likely - /// change over time. + /// Display status. /// - /// ## Programmatically detecting whether the system is deployed via bootc - /// - /// Invoke e.g. `bootc status --json`, and check if `status.booted` is not `null`. + /// Shows bootc system state. Outputs YAML by default, human-readable if terminal detected. Status(StatusOpts), - /// Adds a transient writable overlayfs on `/usr` that will be discarded on reboot. - /// - /// ## Use cases - /// - /// A common pattern is wanting to use tracing/debugging tools, such as `strace` - /// that may not be in the base image. A system package manager such as `apt` or - /// `dnf` can apply changes into this transient overlay that will be discarded on - /// reboot. - /// - /// ## /etc and /var - /// - /// However, this command has no effect on `/etc` and `/var` - changes written - /// there will persist. It is common for package installations to modify these - /// directories. - /// - /// ## Unmounting - /// - /// Almost always, a system process will hold a reference to the open mount point. - /// You can however invoke `umount -l /usr` to perform a "lazy unmount". + /// Add a transient writable overlayfs on `/usr`. /// + /// Allows temporary package installation that will be discarded on reboot. #[clap(alias = "usroverlay")] UsrOverlay, /// Install the running container to a target. /// - /// ## Understanding installations - /// - /// OCI containers are effectively layers of tarballs with JSON for metadata; they - /// cannot be booted directly. The `bootc install` flow is a highly opinionated - /// method to take the contents of the container image and install it to a target - /// block device (or an existing filesystem) in such a way that it can be booted. - /// - /// For example, a Linux partition table and filesystem is used, and the bootloader and kernel - /// embedded in the container image are also prepared. - /// - /// A bootc installed container currently uses OSTree as a backend, and this sets - /// it up such that a subsequent `bootc upgrade` can perform in-place updates. - /// - /// An installation is not simply a copy of the container filesystem, but includes - /// other setup and metadata. + /// Takes a container image and installs it to disk in a bootable format. #[clap(subcommand)] Install(InstallOpts), /// Operations which can be executed as part of a container build. #[clap(subcommand)] Container(ContainerOpts), - /// Operations on container images + /// Operations on container images. /// - /// Stability: This interface is not declared stable and may change or be removed - /// at any point in the future. + /// Stability: This interface may change in the future. #[clap(subcommand, hide = true)] Image(ImageOpts), /// Execute the given command in the host mount namespace @@ -704,9 +646,6 @@ pub(crate) enum Opt { #[clap(subcommand)] #[clap(hide = true)] Internals(InternalsOpts), - #[clap(hide(true))] - #[cfg(feature = "docgen")] - Man(ManOpts), } /// Ensure we've entered a mount namespace, so that we can remount @@ -1499,6 +1438,14 @@ async fn run_from_opt(opt: Opt) -> Result<()> { } #[cfg(feature = "rhsm")] InternalsOpts::PublishRhsmFacts => crate::rhsm::publish_facts(&root).await, + #[cfg(feature = "docgen")] + InternalsOpts::DumpCliJson => { + use clap::CommandFactory; + let cmd = Opt::command(); + let json = crate::cli_json::dump_cli_json(&cmd)?; + println!("{}", json); + Ok(()) + } InternalsOpts::DirDiff { pristine_etc, current_etc, @@ -1522,8 +1469,6 @@ async fn run_from_opt(opt: Opt) -> Result<()> { Ok(()) } }, - #[cfg(feature = "docgen")] - Opt::Man(manopts) => crate::docgen::generate_manpages(&manopts.directory), Opt::State(opts) => match opts { StateOpts::WipeOstree => { let sysroot = ostree::Sysroot::new_default(); diff --git a/crates/lib/src/cli_json.rs b/crates/lib/src/cli_json.rs new file mode 100644 index 000000000..d1017ef0d --- /dev/null +++ b/crates/lib/src/cli_json.rs @@ -0,0 +1,127 @@ +//! Export CLI structure as JSON for documentation generation + +use clap::Command; +use serde::{Deserialize, Serialize}; + +/// Representation of a CLI option for JSON export +#[derive(Debug, Serialize, Deserialize)] +pub struct CliOption { + pub long: String, + pub short: Option, + pub value_name: Option, + pub default: Option, + pub help: String, + pub possible_values: Vec, + pub required: bool, +} + +/// Representation of a CLI command for JSON export +#[derive(Debug, Serialize, Deserialize)] +pub struct CliCommand { + pub name: String, + pub about: Option, + pub options: Vec, + pub positionals: Vec, + pub subcommands: Vec, +} + +/// Representation of a positional argument +#[derive(Debug, Serialize, Deserialize)] +pub struct CliPositional { + pub name: String, + pub help: Option, + pub required: bool, + pub multiple: bool, +} + +/// Convert a clap Command to our JSON representation +pub fn command_to_json(cmd: &Command) -> CliCommand { + let mut options = Vec::new(); + let mut positionals = Vec::new(); + + // Extract arguments + for arg in cmd.get_arguments() { + let id = arg.get_id().as_str(); + + // Skip built-in help and version + if id == "help" || id == "version" { + continue; + } + + // Skip hidden arguments + if arg.is_hide_set() { + continue; + } + + if arg.is_positional() { + // Handle positional arguments + positionals.push(CliPositional { + name: id.to_string(), + help: arg.get_help().map(|h| h.to_string()), + required: arg.is_required_set(), + multiple: false, // For now, simplify this + }); + } else { + // Handle options/flags + let mut possible_values = Vec::new(); + let pvs = arg.get_possible_values(); + if !pvs.is_empty() { + for pv in pvs { + possible_values.push(pv.get_name().to_string()); + } + } + + let help = arg.get_help().map(|h| h.to_string()).unwrap_or_default(); + + options.push(CliOption { + long: arg + .get_long() + .map(String::from) + .unwrap_or_else(|| id.to_string()), + short: arg.get_short().map(|c| c.to_string()), + value_name: arg + .get_value_names() + .and_then(|names| names.first()) + .map(|s| s.to_string()), + default: arg + .get_default_values() + .first() + .and_then(|v| v.to_str()) + .map(String::from), + help, + possible_values, + required: arg.is_required_set(), + }); + } + } + + // Extract subcommands + let mut subcommands = Vec::new(); + for subcmd in cmd.get_subcommands() { + // Skip help subcommand + if subcmd.get_name() == "help" { + continue; + } + + // Skip hidden subcommands + if subcmd.is_hide_set() { + continue; + } + + subcommands.push(command_to_json(subcmd)); + } + + CliCommand { + name: cmd.get_name().to_string(), + about: cmd.get_about().map(|s| s.to_string()), + options, + positionals, + subcommands, + } +} + +/// Dump the entire CLI structure as JSON +pub fn dump_cli_json(cmd: &Command) -> Result { + let cli_structure = command_to_json(cmd); + serde_json::to_string_pretty(&cli_structure) +} diff --git a/crates/lib/src/docgen.rs b/crates/lib/src/docgen.rs deleted file mode 100644 index 53ddc81b4..000000000 --- a/crates/lib/src/docgen.rs +++ /dev/null @@ -1,55 +0,0 @@ -// Copyright 2022 Red Hat, Inc. -// -// SPDX-License-Identifier: Apache-2.0 OR MIT - -use std::fs::OpenOptions; -use std::io::Write; - -use anyhow::{Context, Result}; -use camino::Utf8Path; -use clap::{Command, CommandFactory}; - -pub fn generate_manpages(directory: &Utf8Path) -> Result<()> { - generate_one(directory, crate::cli::Opt::command()) -} - -fn generate_one(directory: &Utf8Path, cmd: Command) -> Result<()> { - let version = env!("CARGO_PKG_VERSION"); - let name = cmd.get_name(); - let bin_name = cmd.get_bin_name().unwrap_or_else(|| name); - let path = directory.join(format!("{name}.8")); - println!("Generating {path}..."); - - let mut out = OpenOptions::new() - .create(true) - .write(true) - .truncate(true) - .open(&path) - .with_context(|| format!("opening {path}")) - .map(std::io::BufWriter::new)?; - clap_mangen::Man::new(cmd.clone()) - .section("8") - .source(format!("bootc {version}")) - .render(&mut out) - .with_context(|| format!("rendering {name}.8"))?; - out.flush().context("flushing man page")?; - drop(out); - - for subcmd in cmd.get_subcommands().filter(|c| !c.is_hide_set()) { - let subname = format!("{}-{}", name, subcmd.get_name()); - let bin_name = format!("{} {}", bin_name, subcmd.get_name()); - // SAFETY: Latest clap 4 requires names are &'static - this is - // not long-running production code, so we just leak the names here. - let subname = &*std::boxed::Box::leak(subname.into_boxed_str()); - let bin_name = &*std::boxed::Box::leak(bin_name.into_boxed_str()); - let subcmd = subcmd - .clone() - .name(subname) - .alias(subname) - .bin_name(bin_name) - .version(version) - .disable_version_flag(true); - generate_one(directory, subcmd.clone().name(subname).version(version))?; - } - Ok(()) -} diff --git a/crates/lib/src/lib.rs b/crates/lib/src/lib.rs index 9c2f87f99..e465a84da 100644 --- a/crates/lib/src/lib.rs +++ b/crates/lib/src/lib.rs @@ -29,7 +29,7 @@ mod task; mod utils; #[cfg(feature = "docgen")] -mod docgen; +mod cli_json; mod bootloader; mod containerenv; diff --git a/crates/xtask/Cargo.toml b/crates/xtask/Cargo.toml index 41542e88d..a703fbfd8 100644 --- a/crates/xtask/Cargo.toml +++ b/crates/xtask/Cargo.toml @@ -17,6 +17,8 @@ anyhow = { workspace = true } camino = { workspace = true } chrono = { workspace = true, features = ["std"] } fn-error-context = { workspace = true } +serde = { workspace = true, features = ["derive"] } +serde_json = { workspace = true } tempfile = { workspace = true } toml = { workspace = true } xshell = { workspace = true } diff --git a/crates/xtask/src/man.rs b/crates/xtask/src/man.rs new file mode 100644 index 000000000..42ba4b7c9 --- /dev/null +++ b/crates/xtask/src/man.rs @@ -0,0 +1,597 @@ +//! Man page generation and synchronization +//! +//! This module handles both the generation of man pages from markdown sources +//! and the synchronization of CLI options from Rust code to those markdown templates. + +use anyhow::{Context, Result}; +use camino::Utf8Path; +use serde::{Deserialize, Serialize}; +use std::fs; +use xshell::{cmd, Shell}; + +/// Represents a CLI option extracted from the JSON dump +#[derive(Debug, Serialize, Deserialize)] +pub struct CliOption { + /// The long flag (e.g., "wipe", "block-setup") + pub long: String, + /// The short flag if any (e.g., "h") + pub short: Option, + /// The value name if the option takes an argument + pub value_name: Option, + /// The default value if any + pub default: Option, + /// The help text (doc comment from Rust) + pub help: String, + /// Possible values for enums + pub possible_values: Vec, + /// Whether the option is required + pub required: bool, +} + +/// Represents a CLI command from the JSON dump +#[derive(Debug, Serialize, Deserialize)] +pub struct CliCommand { + pub name: String, + pub about: Option, + pub options: Vec, + pub positionals: Vec, + pub subcommands: Vec, +} + +/// Represents a positional argument +#[derive(Debug, Serialize, Deserialize)] +pub struct CliPositional { + pub name: String, + pub help: Option, + pub required: bool, + pub multiple: bool, +} + +/// Extract CLI structure by running the JSON dump command +pub fn extract_cli_json(sh: &Shell) -> Result { + let json_output = cmd!(sh, "cargo run --features=docgen -- internals dump-cli-json") + .read() + .context("Running CLI JSON dump command")?; + + let cli_structure: CliCommand = + serde_json::from_str(&json_output).context("Parsing CLI JSON output")?; + + Ok(cli_structure) +} + +/// Find a subcommand by path +pub fn find_subcommand<'a>(cli: &'a CliCommand, path: &[&str]) -> Option<&'a CliCommand> { + if path.is_empty() { + return Some(cli); + } + + let first = path[0]; + let rest = &path[1..]; + + cli.subcommands + .iter() + .find(|cmd| cmd.name == first) + .and_then(|cmd| find_subcommand(cmd, rest)) +} + +/// Convert CLI subcommands to markdown table format (like podman) +fn format_subcommands_as_table(subcommands: &[CliCommand], parent_path: &[&str]) -> String { + if subcommands.is_empty() { + return String::new(); + } + + let mut result = String::new(); + + // Table header + result.push_str("| Command | Description |\n"); + result.push_str("|---------|-------------|\n"); + + // Table rows + for subcmd in subcommands { + let mut full_path = vec!["bootc"]; + full_path.extend_from_slice(parent_path); + full_path.push(&subcmd.name); + + let cmd_name = format!("**{}**", full_path.join(" ")); + let description = subcmd.about.as_deref().unwrap_or("").trim_end_matches('.'); + result.push_str(&format!("| {} | {} |\n", cmd_name, description)); + } + + result.push('\n'); + result +} + +/// Convert CLI options to markdown format +fn format_options_as_markdown(options: &[CliOption], positionals: &[CliPositional]) -> String { + let mut result = String::new(); + + // Format positional arguments first + for pos in positionals { + let name = pos.name.to_uppercase(); + result.push_str(&format!("**{}**\n\n", name)); + + if let Some(help) = &pos.help { + result.push_str(&format!(" {}\n\n", help)); + } + + if pos.required { + result.push_str(" This argument is required.\n\n"); + } + } + + // Format options + for opt in options { + let mut flag_line = String::new(); + + // Add short flag if available + if let Some(short) = &opt.short { + flag_line.push_str(&format!("**-{}**", short)); + flag_line.push_str(", "); + } + + // Add long flag + flag_line.push_str(&format!("**--{}**", opt.long)); + + // Add value name if option takes argument + if let Some(value_name) = &opt.value_name { + flag_line.push_str(&format!("=*{}*", value_name)); + } + + result.push_str(&format!("{}\n\n", flag_line)); + result.push_str(&format!(" {}\n\n", opt.help)); + + // Add possible values for enums + if !opt.possible_values.is_empty() { + result.push_str(" Possible values:\n"); + for value in &opt.possible_values { + result.push_str(&format!(" - {}\n", value)); + } + result.push('\n'); + } + + // Add default value if present + if let Some(default) = &opt.default { + result.push_str(&format!(" Default: {}\n\n", default)); + } + } + + result +} + +/// Update markdown file with generated subcommands +pub fn update_markdown_with_subcommands( + markdown_path: &Utf8Path, + subcommands: &[CliCommand], + parent_path: &[&str], +) -> Result<()> { + let content = + fs::read_to_string(markdown_path).with_context(|| format!("Reading {}", markdown_path))?; + + let begin_marker = ""; + let end_marker = ""; + + let Some((before, rest)) = content.split_once(begin_marker) else { + return Ok(()); // Skip files without markers + }; + + let Some((_, after)) = rest.split_once(end_marker) else { + anyhow::bail!( + "Found BEGIN SUBCOMMANDS marker but not END marker in {}", + markdown_path + ); + }; + + let generated_subcommands = format_subcommands_as_table(subcommands, parent_path); + + // Trim trailing whitespace from before section and ensure exactly one blank line + let before = before.trim_end(); + + let new_content = format!( + "{}\n\n{}\n{}{}{}", + before, begin_marker, generated_subcommands, end_marker, after + ); + + fs::write(markdown_path, new_content) + .with_context(|| format!("Writing to {}", markdown_path))?; + + println!("Updated subcommands in {}", markdown_path); + Ok(()) +} + +/// Update markdown file with generated options +pub fn update_markdown_with_options( + markdown_path: &Utf8Path, + options: &[CliOption], + positionals: &[CliPositional], +) -> Result<()> { + let content = + fs::read_to_string(markdown_path).with_context(|| format!("Reading {}", markdown_path))?; + + let begin_marker = ""; + let end_marker = ""; + + let Some((before, rest)) = content.split_once(begin_marker) else { + return Ok(()); // Skip files without markers + }; + + let Some((_, after)) = rest.split_once(end_marker) else { + anyhow::bail!("Found BEGIN marker but not END marker in {}", markdown_path); + }; + + let generated_options = format_options_as_markdown(options, positionals); + + // Trim trailing whitespace from before section + let mut before = before.trim_end(); + + // Remove # OPTIONS header if it's right before the marker + if before.ends_with("# OPTIONS") { + before = before.strip_suffix("# OPTIONS").unwrap().trim_end(); + } + + // Only add OPTIONS header if there are options or positionals + let new_content = if !options.is_empty() || !positionals.is_empty() { + format!( + "{}\n\n# OPTIONS\n\n{}\n{}{}{}", + before, begin_marker, generated_options, end_marker, after + ) + } else { + format!("{}\n\n{}\n{}{}", before, begin_marker, end_marker, after) + }; + + fs::write(markdown_path, new_content) + .with_context(|| format!("Writing to {}", markdown_path))?; + + println!("Updated {}", markdown_path); + Ok(()) +} + +/// Discover man page files and infer their command paths from filenames +fn discover_man_page_mappings( + cli_structure: &CliCommand, +) -> Result>)>> { + let man_dir = Utf8Path::new("docs/src/man"); + let mut mappings = Vec::new(); + + // Read all .md files in the man directory + for entry in fs::read_dir(man_dir)? { + let entry = entry?; + let path = entry.path(); + + if let Some(extension) = path.extension() { + if extension != "md" { + continue; + } + } else { + continue; + } + + let filename = path + .file_name() + .and_then(|n| n.to_str()) + .ok_or_else(|| anyhow::anyhow!("Invalid filename"))?; + + // Check if the file contains generation markers + let content = fs::read_to_string(&path)?; + if !content.contains("") + && !content.contains("") + { + continue; + } + + // Infer command path from filename by matching against CLI structure + let command_path = if let Some(cmd_part) = filename + .strip_prefix("bootc-") + .and_then(|s| s.strip_suffix(".md")) + .and_then(|s| s.rsplit_once('.').map(|(name, _section)| name)) + { + let path = find_command_path_for_filename(cli_structure, cmd_part); + path + } else { + None + }; + + mappings.push((filename.to_string(), command_path)); + } + + Ok(mappings) +} + +/// Find the command path for a filename by searching the CLI structure +fn find_command_path_for_filename( + cli_structure: &CliCommand, + filename_part: &str, +) -> Option> { + // First, try to match top-level commands + if let Some(subcommand) = cli_structure + .subcommands + .iter() + .find(|cmd| cmd.name == filename_part) + { + return Some(vec![subcommand.name.clone()]); + } + + // Then, try to match subcommands with pattern COMMAND-SUBCOMMAND + for subcommand in &cli_structure.subcommands { + for sub_subcommand in &subcommand.subcommands { + let expected_pattern = format!("{}-{}", subcommand.name, sub_subcommand.name); + if expected_pattern == filename_part { + return Some(vec![subcommand.name.clone(), sub_subcommand.name.clone()]); + } + } + } + + None +} + +/// Sync all man pages with their corresponding CLI commands +pub fn sync_all_man_pages(sh: &Shell) -> Result<()> { + let cli_structure = extract_cli_json(sh)?; + + // Discover man page files automatically + let mappings = discover_man_page_mappings(&cli_structure)?; + + for (filename, subcommand_path) in mappings { + let markdown_path = Utf8Path::new("docs/src/man").join(&filename); + + if !markdown_path.exists() { + continue; + } + + // Navigate to the right subcommand + let target_cmd = if let Some(ref path) = subcommand_path { + let path_refs: Vec<&str> = path.iter().map(|s| s.as_str()).collect(); + find_subcommand(&cli_structure, &path_refs) + .ok_or_else(|| anyhow::anyhow!("Subcommand {:?} not found", path))? + } else { + &cli_structure + }; + + // Update options if the file has options markers + let content = fs::read_to_string(&markdown_path)?; + if content.contains("") { + update_markdown_with_options( + &markdown_path, + &target_cmd.options, + &target_cmd.positionals, + )?; + } + + // Update subcommands if the file has subcommands markers + if content.contains("") { + let parent_path: Vec<&str> = if let Some(path) = &subcommand_path { + path.iter().map(|s| s.as_str()).collect() + } else { + vec![] + }; + update_markdown_with_subcommands( + &markdown_path, + &target_cmd.subcommands, + &parent_path, + )?; + } + } + + Ok(()) +} + +/// Test the sync workflow +pub fn test_sync_workflow(sh: &Shell) -> Result<()> { + println!("๐Ÿงช Testing man page sync workflow..."); + + // Create a backup of current files + let test_dir = "target/test-sync"; + sh.create_dir(test_dir)?; + + // Run sync + sync_all_man_pages(sh)?; + + println!("โœ… Sync workflow test completed successfully"); + Ok(()) +} + +/// Generate man pages from hand-written markdown sources +pub fn generate_man_pages(sh: &Shell) -> Result<()> { + let man_src_dir = Utf8Path::new("docs/src/man"); + let man_output_dir = Utf8Path::new("target/man"); + + // Ensure output directory exists + sh.create_dir(man_output_dir)?; + + // First, sync the markdown files with current CLI options + sync_all_man_pages(sh)?; + + // Get version for replacement during generation + let version = get_package_version()?; + + // Convert each markdown file to man page format + for entry in fs::read_dir(man_src_dir)? { + let entry = entry?; + let path = entry.path(); + + if path.extension().and_then(|s| s.to_str()) != Some("md") { + continue; + } + + let filename = path + .file_stem() + .and_then(|s| s.to_str()) + .ok_or_else(|| anyhow::anyhow!("Invalid filename"))?; + + // Parse section from filename (e.g., bootc.8, bootc-config.5) + // All man page files must have a section number + let (base_name, section) = filename + .rsplit_once('.') + .and_then(|(name, section_str)| { + section_str.parse::().ok().map(|section| (name, section)) + }) + .ok_or_else(|| anyhow::anyhow!("Man page filename must include section number (e.g., bootc.8.md, bootc-config.5.md): {}.md", filename))?; + + let output_file = man_output_dir.join(format!("{}.{}", base_name, section)); + + // Read markdown content and replace version placeholders + let content = fs::read_to_string(&path)?; + let content_with_version = content.replace("", &version); + + // Create temporary file with version-replaced content + let temp_path = format!("{}.tmp", path.display()); + fs::write(&temp_path, content_with_version)?; + + cmd!(sh, "go-md2man -in {temp_path} -out {output_file}") + .run() + .with_context(|| format!("Converting {} to man page", path.display()))?; + + // Clean up temporary file + fs::remove_file(&temp_path)?; + + println!("Generated {}", output_file); + } + + // Apply post-processing fixes for apostrophe handling + apply_man_page_fixes(sh, man_output_dir)?; + + Ok(()) +} + +/// Get version from Cargo.toml +fn get_package_version() -> Result { + let cargo_toml = + fs::read_to_string("crates/lib/Cargo.toml").context("Reading crates/lib/Cargo.toml")?; + + let parsed: toml::Table = cargo_toml.parse().context("Parsing Cargo.toml")?; + + let version = parsed + .get("package") + .and_then(|p| p.as_table()) + .and_then(|p| p.get("version")) + .and_then(|v| v.as_str()) + .ok_or_else(|| anyhow::anyhow!("Could not find package.version in Cargo.toml"))?; + + Ok(format!("v{}", version)) +} + +/// Single command to update all man pages - auto-discover new commands and sync existing ones +pub fn update_manpages(sh: &Shell) -> Result<()> { + println!("Discovering CLI structure..."); + let cli_structure = extract_cli_json(sh)?; + + println!("Checking for missing man pages..."); + let mut created_count = 0; + + // Auto-discover commands that need man pages + let mut commands_to_check = Vec::new(); + + // Add top-level commands + for cmd in &cli_structure.subcommands { + commands_to_check.push(vec![cmd.name.clone()]); + } + + // Add subcommands + for cmd in &cli_structure.subcommands { + for subcmd in &cmd.subcommands { + commands_to_check.push(vec![cmd.name.clone(), subcmd.name.clone()]); + } + } + + // Check each command and create man page if missing + for command_parts in commands_to_check { + let filename = if command_parts.len() == 1 { + format!("bootc-{}.md", command_parts[0]) + } else { + format!("bootc-{}.md", command_parts.join("-")) + }; + + let filepath = format!("docs/src/man/{}", filename); + + if !std::path::Path::new(&filepath).exists() { + // Find the command in CLI structure + let command_parts_refs: Vec<&str> = command_parts.iter().map(|s| s.as_str()).collect(); + let target_cmd = find_subcommand(&cli_structure, &command_parts_refs); + + if let Some(cmd) = target_cmd { + let command_name_full = format!("bootc {}", command_parts.join(" ")); + let command_description = cmd.about.as_deref().unwrap_or("TODO: Add description"); + + let template = format!( + r#"# NAME + +{} - {} + +# SYNOPSIS + +**{}** [*OPTIONS*] + +# DESCRIPTION + +{} + + + + + +# EXAMPLES + +TODO: Add practical examples showing how to use this command. + +# SEE ALSO + +**bootc**(8) + +# VERSION + + +"#, + command_name_full.replace(" ", "-"), + command_description, + command_name_full, + command_description + ); + + std::fs::write(&filepath, template) + .with_context(|| format!("Writing template to {}", filepath))?; + + println!("โœ“ Created man page template: {}", filepath); + created_count += 1; + } + } + } + + if created_count > 0 { + println!("โœ“ Created {} new man page templates", created_count); + } else { + println!("โœ“ All commands already have man pages"); + } + + println!("๐Ÿ”„ Syncing OPTIONS sections..."); + sync_all_man_pages(sh)?; + + println!("Man pages updated."); + println!(""); + println!("Next steps for new templates:"); + println!(" - Edit the templates to add detailed descriptions and examples"); + println!(" - Run 'cargo xtask manpages' to generate final man pages"); + + Ok(()) +} + +/// Apply post-processing fixes to generated man pages +fn apply_man_page_fixes(sh: &Shell, dir: &Utf8Path) -> Result<()> { + // Fix apostrophe rendering issue + for entry in fs::read_dir(dir)? { + let entry = entry?; + let path = entry.path(); + + if path + .extension() + .and_then(|s| s.to_str()) + .map_or(false, |e| e.chars().all(|c| c.is_numeric())) + { + // Apply the same sed fixes as before + let groffsub = r"1i .ds Aq \\(aq"; + let dropif = r"/\.g \.ds Aq/d"; + let dropelse = r"/.el .ds Aq '/d"; + cmd!(sh, "sed -i -e {groffsub} -e {dropif} -e {dropelse} {path}").run()?; + } + } + + Ok(()) +} diff --git a/crates/xtask/src/xtask.rs b/crates/xtask/src/xtask.rs index 831356964..ec42cfeee 100644 --- a/crates/xtask/src/xtask.rs +++ b/crates/xtask/src/xtask.rs @@ -5,11 +5,13 @@ use std::fs::File; use std::io::{BufRead, BufReader, BufWriter, Write}; use std::process::Command; -use anyhow::{anyhow, Context, Result}; +use anyhow::{Context, Result}; use camino::{Utf8Path, Utf8PathBuf}; use fn_error_context::context; use xshell::{cmd, Shell}; +mod man; + const NAME: &str = "bootc"; const TEST_IMAGES: &[&str] = &[ "quay.io/curl/curl-base:latest", @@ -33,7 +35,10 @@ fn main() { #[allow(clippy::type_complexity)] const TASKS: &[(&str, fn(&Shell) -> Result<()>)] = &[ - ("manpages", manpages), + ("manpages", man::generate_man_pages), + ("sync-manpages", man::sync_all_man_pages), + ("test-sync-manpages", man::test_sync_workflow), + ("update-json-schemas", update_json_schemas), ("update-generated", update_generated), ("package", package), ("package-srpm", package_srpm), @@ -95,89 +100,6 @@ fn gitrev(sh: &Shell) -> Result { } } -#[context("Manpages")] -fn manpages(sh: &Shell) -> Result<()> { - // We currently go: clap (Rust) -> man -> markdown for the CLI - sh.create_dir("target/man")?; - cmd!( - sh, - "cargo run --features=docgen -- man --directory target/man" - ) - .run()?; - - // Post-process hack to unconditionally define the roff string for - // apostrophe, See: - // https://github.com/bootc-dev/bootc/pull/1385#discussion_r2172661872 - for page in std::fs::read_dir(sh.current_dir().join("target/man"))? { - let page = page?; - let path = page.path(); - let groffsub = r"1i .ds Aq \\(aq"; - let dropif = r"/\.g \.ds Aq/d"; - let dropelse = r"/.el .ds Aq '/d"; - cmd!(sh, "sed -i -e {groffsub} -e {dropif} -e {dropelse} {path}").run()?; - } - - // We also have some man pages for the systemd units which are canonically - // maintained as markdown; convert them to man pages. - let extradir = sh.current_dir().join("docs/src/man-md"); - for ent in std::fs::read_dir(extradir)? { - let ent = ent?; - let srcpath = ent.path(); - let Some(extension) = srcpath.extension() else { - continue; - }; - if extension != "md" { - continue; - } - let base_filename = srcpath - .file_stem() - .and_then(|name| name.to_str()) - .ok_or_else(|| anyhow!("Expected filename in {srcpath:?}"))?; - let src = - std::fs::read_to_string(&srcpath).with_context(|| format!("Reading {srcpath:?}"))?; - let section = 5; - let buf = mandown::convert(&src, base_filename, section); - let target = format!("target/man/{base_filename}.{section}"); - std::fs::write(&target, buf).with_context(|| format!("Writing {target}"))?; - } - Ok(()) -} - -/// Update generated files, such as converting the man pages to markdown. -/// This process is currently manual. -#[context("Updating generated files")] -fn update_generated(sh: &Shell) -> Result<()> { - manpages(sh)?; - // And convert the man pages into markdown, so they can be included - // in the docs. - for ent in std::fs::read_dir("target/man")? { - let ent = ent?; - let path = &ent.path(); - if path.extension().and_then(|s| s.to_str()) != Some("8") { - continue; - } - let filename = path - .file_stem() - .and_then(|name| name.to_str()) - .ok_or_else(|| anyhow!("Expected filename in {path:?}"))?; - let target = format!("docs/src/man/{filename}.md"); - cmd!( - sh, - "pandoc --from=man --to=markdown --output={target} {path}" - ) - .run()?; - } - for (of, target) in [ - ("host", "docs/src/host-v1.schema.json"), - ("progress", "docs/src/progress-v0.schema.json"), - ] { - let schema = cmd!(sh, "cargo run -q -- internals print-json-schema --of={of}").read()?; - std::fs::write(target, &schema)?; - println!("Updated {target}"); - } - Ok(()) -} - #[context("test-integration")] fn all_plan_files(sh: &Shell) -> Result> { // We need to split most of our tests into separate plans because tmt doesn't @@ -301,7 +223,6 @@ fn edit_vendor_config(config: &str) -> Result { #[context("Packaging")] fn impl_package(sh: &Shell) -> Result { let source_date_epoch = git_source_date_epoch(".".into())?; - manpages(sh)?; let v = gitrev(sh)?; let namev = format!("{NAME}-{v}"); @@ -330,12 +251,6 @@ fn impl_package(sh: &Shell) -> Result { ) .run()?; } - // Append our generated man pages. - cmd!( - sh, - "tar -r -C target {TAR_REPRODUCIBLE_OPTS...} --transform=s,^,{prefix}, -f {p} man" - ) - .run()?; // Compress with zstd let srcpath: Utf8PathBuf = format!("{p}.zstd").into(); cmd!(sh, "zstd --rm -f {p} -o {srcpath}").run()?; @@ -464,6 +379,28 @@ fn package_srpm(sh: &Shell) -> Result<()> { Ok(()) } +/// Update JSON schema files +#[context("Updating JSON schemas")] +fn update_json_schemas(sh: &Shell) -> Result<()> { + for (of, target) in [ + ("host", "docs/src/host-v1.schema.json"), + ("progress", "docs/src/progress-v0.schema.json"), + ] { + let schema = cmd!(sh, "cargo run -q -- internals print-json-schema --of={of}").read()?; + std::fs::write(target, &schema)?; + println!("Updated {target}"); + } + Ok(()) +} + +/// Update generated files using the new sync approach +#[context("Updating generated files")] +fn update_generated(sh: &Shell) -> Result<()> { + man::update_manpages(sh)?; + update_json_schemas(sh)?; + Ok(()) +} + fn print_help(_sh: &Shell) -> Result<()> { println!("Tasks:"); for (name, _) in TASKS { diff --git a/docs/src/man/bootc-config.5.md b/docs/src/man/bootc-config.5.md new file mode 100644 index 000000000..07bdfbdf8 --- /dev/null +++ b/docs/src/man/bootc-config.5.md @@ -0,0 +1,57 @@ +# NAME + +bootc-config - Configuration file format for bootc + +# SYNOPSIS + +**/etc/bootc/config.toml** + +# DESCRIPTION + +The bootc configuration file uses TOML format to specify various +settings for bootc operation. + +# FILE FORMAT + +The configuration file is in TOML format with the following sections: + +## [core] + +Core configuration options. + +**auto_updates** = *boolean* + Enable or disable automatic updates. Default: false + +**update_interval** = *string* + Update check interval (e.g., "daily", "weekly"). Default: "weekly" + +## [storage] + +Storage-related configuration. + +**root** = *path* + Root storage path. Default: "/sysroot/ostree" + +# EXAMPLES + +A basic configuration file: + + [core] + auto_updates = true + update_interval = "daily" + + [storage] + root = "/var/lib/bootc" + +# FILES + +**/etc/bootc/config.toml** + System-wide configuration file + +# SEE ALSO + +**bootc**(8), **toml**(5) + +# VERSION + + \ No newline at end of file diff --git a/docs/src/man/bootc-container-lint.8.md b/docs/src/man/bootc-container-lint.8.md new file mode 100644 index 000000000..13f502b99 --- /dev/null +++ b/docs/src/man/bootc-container-lint.8.md @@ -0,0 +1,60 @@ +# NAME + +bootc-container-lint - Perform relatively inexpensive static analysis +checks as part of a container build + +# SYNOPSIS + +**bootc container lint** [*OPTIONS...*] + +# DESCRIPTION + +Perform relatively inexpensive static analysis checks as part of a +container build. + +This is intended to be invoked via e.g. `RUN bootc container lint` as +part of a build process; it will error if any problems are detected. + +# OPTIONS + + +**--rootfs**=*ROOTFS* + + Operate on the provided rootfs + + Default: / + +**--fatal-warnings**=*FATAL_WARNINGS* + + Make warnings fatal + + Possible values: + - true + - false + +**--list**=*LIST* + + Instead of executing the lints, just print all available lints. At the current time, this will output in YAML format because it's reasonably human friendly. However, there is no commitment to maintaining this exact format; do not parse it via code or scripts + + Possible values: + - true + - false + +**--skip**=*SKIP* + + Skip checking the targeted lints, by name. Use `--list` to discover the set of available lints + +**--no-truncate**=*NO_TRUNCATE* + + Don't truncate the output. By default, only a limited number of entries are shown for each lint, followed by a count of remaining entries + + Possible values: + - true + - false + + + +# VERSION + + + diff --git a/docs/src/man/bootc-container-lint.md b/docs/src/man/bootc-container-lint.md deleted file mode 100644 index a6f5d7962..000000000 --- a/docs/src/man/bootc-container-lint.md +++ /dev/null @@ -1,56 +0,0 @@ -# NAME - -bootc-container-lint - Perform relatively inexpensive static analysis -checks as part of a container build - -# SYNOPSIS - -**bootc container lint** \[**\--rootfs**\] \[**\--fatal-warnings**\] -\[**\--list**\] \[**\--skip**\] \[**\--no-truncate**\] -\[**-h**\|**\--help**\] - -# DESCRIPTION - -Perform relatively inexpensive static analysis checks as part of a -container build. - -This is intended to be invoked via e.g. \`RUN bootc container lint\` as -part of a build process; it will error if any problems are detected. - -# OPTIONS - -**\--rootfs** *\* \[default: /\] - -: Operate on the provided rootfs - -**\--fatal-warnings** - -: Make warnings fatal - -**\--list** - -: Instead of executing the lints, just print all available lints. At - the current time, this will output in YAML format because it\'s - reasonably human friendly. However, there is no commitment to - maintaining this exact format; do not parse it via code or scripts - -**\--skip** *\* - -: Skip checking the targeted lints, by name. Use \`\--list\` to - discover the set of available lints. - - Example: \--skip nonempty-boot \--skip baseimage-root - -**\--no-truncate** - -: Don\'t truncate the output. By default, only a limited number of - entries are shown for each lint, followed by a count of remaining - entries - -**-h**, **\--help** - -: Print help (see a summary with \'-h\') - -# VERSION - -v1.8.0 diff --git a/docs/src/man/bootc-container.8.md b/docs/src/man/bootc-container.8.md new file mode 100644 index 000000000..9eeab9a90 --- /dev/null +++ b/docs/src/man/bootc-container.8.md @@ -0,0 +1,29 @@ +# NAME + +bootc-container - Operations which can be executed as part of a +container build + +# SYNOPSIS + +**bootc container** [*OPTIONS...*] <*SUBCOMMAND*> + +# DESCRIPTION + +Operations which can be executed as part of a container build + + + + +# SUBCOMMANDS + + +| Command | Description | +|---------|-------------| +| **bootc container lint** | Perform relatively inexpensive static analysis checks as part of a container build | + + + +# VERSION + + + diff --git a/docs/src/man/bootc-container.md b/docs/src/man/bootc-container.md deleted file mode 100644 index 0541e8d2f..000000000 --- a/docs/src/man/bootc-container.md +++ /dev/null @@ -1,33 +0,0 @@ -# NAME - -bootc-container - Operations which can be executed as part of a -container build - -# SYNOPSIS - -**bootc container** \[**-h**\|**\--help**\] \<*subcommands*\> - -# DESCRIPTION - -Operations which can be executed as part of a container build - -# OPTIONS - -**-h**, **\--help** - -: Print help - -# SUBCOMMANDS - -bootc-container-lint(8) - -: Perform relatively inexpensive static analysis checks as part of a - container build - -bootc-container-help(8) - -: Print this message or the help of the given subcommand(s) - -# VERSION - -v1.8.0 diff --git a/docs/src/man/bootc-edit.8.md b/docs/src/man/bootc-edit.8.md new file mode 100644 index 000000000..dcf0581da --- /dev/null +++ b/docs/src/man/bootc-edit.8.md @@ -0,0 +1,42 @@ +# NAME + +bootc-edit - Apply full changes to the host specification + +# SYNOPSIS + +**bootc edit** [*OPTIONS...*] + +# DESCRIPTION + +Apply full changes to the host specification. + +This command operates very similarly to `kubectl apply`; if invoked +interactively, then the current host specification will be presented in +the system default `\$EDITOR` for interactive changes. + +It is also possible to directly provide new contents via `bootc edit +\--filename`. + +Only changes to the `spec` section are honored. + +# OPTIONS + + +**-f**, **--filename**=*FILENAME* + + Use filename to edit system specification + +**--quiet**=*QUIET* + + Don't display progress + + Possible values: + - true + - false + + + +# VERSION + + + diff --git a/docs/src/man/bootc-edit.md b/docs/src/man/bootc-edit.md deleted file mode 100644 index 79e1b08e6..000000000 --- a/docs/src/man/bootc-edit.md +++ /dev/null @@ -1,39 +0,0 @@ -# NAME - -bootc-edit - Apply full changes to the host specification - -# SYNOPSIS - -**bootc edit** \[**-f**\|**\--filename**\] \[**\--quiet**\] -\[**-h**\|**\--help**\] - -# DESCRIPTION - -Apply full changes to the host specification. - -This command operates very similarly to \`kubectl apply\`; if invoked -interactively, then the current host specification will be presented in -the system default \`\$EDITOR\` for interactive changes. - -It is also possible to directly provide new contents via \`bootc edit -\--filename\`. - -Only changes to the \`spec\` section are honored. - -# OPTIONS - -**-f**, **\--filename** *\* - -: Use filename to edit system specification - -**\--quiet** - -: Don\'t display progress - -**-h**, **\--help** - -: Print help (see a summary with \'-h\') - -# VERSION - -v1.8.0 diff --git a/docs/src/man-md/bootc-fetch-apply-updates.service.md b/docs/src/man/bootc-fetch-apply-updates.service.5.md similarity index 93% rename from docs/src/man-md/bootc-fetch-apply-updates.service.md rename to docs/src/man/bootc-fetch-apply-updates.service.5.md index ed16ce8b9..50da19408 100644 --- a/docs/src/man-md/bootc-fetch-apply-updates.service.md +++ b/docs/src/man/bootc-fetch-apply-updates.service.5.md @@ -30,4 +30,8 @@ are: # SEE ALSO -**bootc(1)** \ No newline at end of file +**bootc(1)** + +# VERSION + + \ No newline at end of file diff --git a/docs/src/man-md/bootc-install-config.md b/docs/src/man/bootc-install-config.5.md similarity index 97% rename from docs/src/man-md/bootc-install-config.md rename to docs/src/man/bootc-install-config.5.md index 4f1313521..d52b75ce6 100644 --- a/docs/src/man-md/bootc-install-config.md +++ b/docs/src/man/bootc-install-config.5.md @@ -51,3 +51,7 @@ kargs = ["nosmt", "console=tty0"] # SEE ALSO **bootc(1)** + +# VERSION + + diff --git a/docs/src/man/bootc-install-config.md b/docs/src/man/bootc-install-config.md deleted file mode 100644 index 82321666f..000000000 --- a/docs/src/man/bootc-install-config.md +++ /dev/null @@ -1 +0,0 @@ -# man bootc-install-config diff --git a/docs/src/man/bootc-install-ensure-completion.md b/docs/src/man/bootc-install-ensure-completion.8.md similarity index 67% rename from docs/src/man/bootc-install-ensure-completion.md rename to docs/src/man/bootc-install-ensure-completion.8.md index f0be20d0e..9977882ff 100644 --- a/docs/src/man/bootc-install-ensure-completion.md +++ b/docs/src/man/bootc-install-ensure-completion.8.md @@ -5,7 +5,7 @@ are performing an ostree-based installation, not bootc # SYNOPSIS -**bootc install ensure-completion** \[**-h**\|**\--help**\] +**bootc install ensure-completion** [*OPTIONS...*] # DESCRIPTION @@ -15,16 +15,14 @@ installation, not bootc. In this scenario the installation may be missing bootc specific features such as kernel arguments, logically bound images and more. This command can be used to attempt to reconcile. At the current time, the only -tested environment is Anaconda using \`ostreecontainer\` and it is +tested environment is Anaconda using `ostreecontainer` and it is recommended to avoid usage outside of that environment. Instead, ensure -your code is using \`bootc install to-filesystem\` from the start. +your code is using `bootc install to-filesystem` from the start. -# OPTIONS - -**-h**, **\--help** - -: Print help (see a summary with \'-h\') + + # VERSION -v1.8.0 + + diff --git a/docs/src/man/bootc-install-finalize.8.md b/docs/src/man/bootc-install-finalize.8.md new file mode 100644 index 000000000..5101c6e6e --- /dev/null +++ b/docs/src/man/bootc-install-finalize.8.md @@ -0,0 +1,35 @@ +# NAME + +bootc-install-finalize - Execute this as the penultimate step of an +installation using `install to-filesystem` + +# SYNOPSIS + +**bootc install finalize** [*OPTIONS...*] <*ROOT_PATH*> + +# DESCRIPTION + +Execute this as the penultimate step of an installation using `install +to-filesystem` + +# OPTIONS + + +**ROOT_PATH** + + Path to the mounted root filesystem + + This argument is required. + + + +# ARGUMENTS + +\<*ROOT_PATH*\> + +: Path to the mounted root filesystem + +# VERSION + + + diff --git a/docs/src/man/bootc-install-finalize.md b/docs/src/man/bootc-install-finalize.md deleted file mode 100644 index 099a7e38d..000000000 --- a/docs/src/man/bootc-install-finalize.md +++ /dev/null @@ -1,27 +0,0 @@ -# NAME - -bootc-install-finalize - Execute this as the penultimate step of an -installation using \`install to-filesystem\` - -# SYNOPSIS - -**bootc install finalize** \[**-h**\|**\--help**\] \<*ROOT_PATH*\> - -# DESCRIPTION - -Execute this as the penultimate step of an installation using \`install -to-filesystem\` - -# OPTIONS - -**-h**, **\--help** - -: Print help - -\<*ROOT_PATH*\> - -: Path to the mounted root filesystem - -# VERSION - -v1.8.0 diff --git a/docs/src/man/bootc-install-print-configuration.8.md b/docs/src/man/bootc-install-print-configuration.8.md new file mode 100644 index 000000000..2057971ff --- /dev/null +++ b/docs/src/man/bootc-install-print-configuration.8.md @@ -0,0 +1,28 @@ +# NAME + +bootc-install-print-configuration - Output JSON to stdout that contains +the merged installation configuration as it may be relevant to calling +processes using `install to-filesystem` that in particular want to +discover the desired root filesystem type from the container image + +# SYNOPSIS + +**bootc install print-configuration** [*OPTIONS...*] + +# DESCRIPTION + +Output JSON to stdout that contains the merged installation +configuration as it may be relevant to calling processes using `install +to-filesystem` that in particular want to discover the desired root +filesystem type from the container image. + +At the current time, the only output key is `root-fs-type` which is a +string-valued filesystem name suitable for passing to `mkfs.\$type`. + + + + +# VERSION + + + diff --git a/docs/src/man/bootc-install-print-configuration.md b/docs/src/man/bootc-install-print-configuration.md deleted file mode 100644 index 14666d4a6..000000000 --- a/docs/src/man/bootc-install-print-configuration.md +++ /dev/null @@ -1,30 +0,0 @@ -# NAME - -bootc-install-print-configuration - Output JSON to stdout that contains -the merged installation configuration as it may be relevant to calling -processes using \`install to-filesystem\` that in particular want to -discover the desired root filesystem type from the container image - -# SYNOPSIS - -**bootc install print-configuration** \[**-h**\|**\--help**\] - -# DESCRIPTION - -Output JSON to stdout that contains the merged installation -configuration as it may be relevant to calling processes using \`install -to-filesystem\` that in particular want to discover the desired root -filesystem type from the container image. - -At the current time, the only output key is \`root-fs-type\` which is a -string-valued filesystem name suitable for passing to \`mkfs.\$type\`. - -# OPTIONS - -**-h**, **\--help** - -: Print help (see a summary with \'-h\') - -# VERSION - -v1.8.0 diff --git a/docs/src/man/bootc-install-to-disk.8.md b/docs/src/man/bootc-install-to-disk.8.md new file mode 100644 index 000000000..951f35280 --- /dev/null +++ b/docs/src/man/bootc-install-to-disk.8.md @@ -0,0 +1,171 @@ +# NAME + +bootc-install-to-disk - Install to the target block device + +# SYNOPSIS + +**bootc install to-disk** [*OPTIONS...*] <*DEVICE*> + +# DESCRIPTION + +Install to the target block device. + +This command must be invoked inside of the container, which will be +installed. The container must be run in `--privileged` mode, and +hence will be able to see all block devices on the system. + +The default storage layout uses the root filesystem type configured in +the container image, alongside any required system partitions such as +the EFI system partition. Use `install to-filesystem` for anything +more complex such as RAID, LVM, LUKS etc. + +# OPTIONS + + +**DEVICE** + + Target block device for installation. The entire device will be wiped + + This argument is required. + +**--wipe**=*WIPE* + + Automatically wipe all existing data on device + + Possible values: + - true + - false + +**--block-setup**=*BLOCK_SETUP* + + Target root block device setup + + Possible values: + - direct + - tpm2-luks + +**--filesystem**=*FILESYSTEM* + + Target root filesystem type + + Possible values: + - xfs + - ext4 + - btrfs + +**--root-size**=*ROOT_SIZE* + + Size of the root partition (default specifier: M). Allowed specifiers: M (mebibytes), G (gibibytes), T (tebibytes) + +**--source-imgref**=*SOURCE_IMGREF* + + Install the system from an explicitly given source + +**--target-transport**=*TARGET_TRANSPORT* + + The transport; e.g. oci, oci-archive, containers-storage. Defaults to `registry` + + Default: registry + +**--target-imgref**=*TARGET_IMGREF* + + Specify the image to fetch for subsequent updates + +**--enforce-container-sigpolicy**=*ENFORCE_CONTAINER_SIGPOLICY* + + This is the inverse of the previous `--target-no-signature-verification` (which is now a no-op). Enabling this option enforces that `/etc/containers/policy.json` includes a default policy which requires signatures + + Possible values: + - true + - false + +**--run-fetch-check**=*RUN_FETCH_CHECK* + + Verify the image can be fetched from the bootc image. Updates may fail when the installation host is authenticated with the registry but the pull secret is not in the bootc image + + Possible values: + - true + - false + +**--skip-fetch-check**=*SKIP_FETCH_CHECK* + + Verify the image can be fetched from the bootc image. Updates may fail when the installation host is authenticated with the registry but the pull secret is not in the bootc image + + Possible values: + - true + - false + +**--disable-selinux**=*DISABLE_SELINUX* + + Disable SELinux in the target (installed) system + + Possible values: + - true + - false + +**--karg**=*KARG* + + Add a kernel argument. This option can be provided multiple times + +**--root-ssh-authorized-keys**=*ROOT_SSH_AUTHORIZED_KEYS* + + The path to an `authorized_keys` that will be injected into the `root` account + +**--generic-image**=*GENERIC_IMAGE* + + Perform configuration changes suitable for a "generic" disk image. At the moment: + + Possible values: + - true + - false + +**--bound-images**=*BOUND_IMAGES* + + How should logically bound images be retrieved + + Possible values: + - stored + - skip + - pull + + Default: stored + +**--stateroot**=*STATEROOT* + + The stateroot name to use. Defaults to `default` + +**--via-loopback**=*VIA_LOOPBACK* + + Instead of targeting a block device, write to a file via loopback + + Possible values: + - true + - false + + + +# EXAMPLES + +Install to a disk, wiping all existing data: + + bootc install to-disk --wipe /dev/sda + +Install with a specific root filesystem type: + + bootc install to-disk --filesystem xfs /dev/nvme0n1 + +Install with TPM2 LUKS encryption: + + bootc install to-disk --block-setup tpm2-luks /dev/sda + +Install with custom kernel arguments: + + bootc install to-disk --karg=nosmt --karg=console=ttyS0 /dev/sda + +# SEE ALSO + +**bootc**(8), **bootc-install**(8), **bootc-install-to-filesystem**(8) + +# VERSION + + diff --git a/docs/src/man/bootc-install-to-disk.md b/docs/src/man/bootc-install-to-disk.md deleted file mode 100644 index f3d45dd2b..000000000 --- a/docs/src/man/bootc-install-to-disk.md +++ /dev/null @@ -1,163 +0,0 @@ -# NAME - -bootc-install-to-disk - Install to the target block device - -# SYNOPSIS - -**bootc install to-disk** \[**\--wipe**\] \[**\--block-setup**\] -\[**\--filesystem**\] \[**\--root-size**\] \[**\--source-imgref**\] -\[**\--target-transport**\] \[**\--target-imgref**\] -\[**\--enforce-container-sigpolicy**\] \[**\--run-fetch-check**\] -\[**\--skip-fetch-check**\] \[**\--disable-selinux**\] \[**\--karg**\] -\[**\--root-ssh-authorized-keys**\] \[**\--generic-image**\] -\[**\--bound-images**\] \[**\--stateroot**\] \[**\--via-loopback**\] -\[**-h**\|**\--help**\] \<*DEVICE*\> - -# DESCRIPTION - -Install to the target block device. - -This command must be invoked inside of the container, which will be -installed. The container must be run in \`\--privileged\` mode, and -hence will be able to see all block devices on the system. - -The default storage layout uses the root filesystem type configured in -the container image, alongside any required system partitions such as -the EFI system partition. Use \`install to-filesystem\` for anything -more complex such as RAID, LVM, LUKS etc. - -# OPTIONS - -**\--wipe** - -: Automatically wipe all existing data on device - -**\--block-setup** *\* - -: Target root block device setup. - - direct: Filesystem written directly to block device tpm2-luks: Bind - unlock of filesystem to presence of the default tpm2 device.\ - - \ - \[*possible values: *direct, tpm2-luks\] - -**\--filesystem** *\* - -: Target root filesystem type\ - - \ - \[*possible values: *xfs, ext4, btrfs\] - -**\--root-size** *\* - -: Size of the root partition (default specifier: M). Allowed - specifiers: M (mebibytes), G (gibibytes), T (tebibytes). - - By default, all remaining space on the disk will be used. - -**\--source-imgref** *\* - -: Install the system from an explicitly given source. - - By default, bootc install and install-to-filesystem assumes that it - runs in a podman container, and it takes the container image to - install from the podman\'s container registry. If \--source-imgref - is given, bootc uses it as the installation source, instead of the - behaviour explained in the previous paragraph. See skopeo(1) for - accepted formats. - -**\--target-transport** *\* \[default: registry\] - -: The transport; e.g. oci, oci-archive, containers-storage. Defaults - to \`registry\` - -**\--target-imgref** *\* - -: Specify the image to fetch for subsequent updates - -**\--enforce-container-sigpolicy** - -: This is the inverse of the previous - \`\--target-no-signature-verification\` (which is now a no-op). - Enabling this option enforces that \`/etc/containers/policy.json\` - includes a default policy which requires signatures - -**\--run-fetch-check** - -: Verify the image can be fetched from the bootc image. Updates may - fail when the installation host is authenticated with the registry - but the pull secret is not in the bootc image - -**\--skip-fetch-check** - -: Verify the image can be fetched from the bootc image. Updates may - fail when the installation host is authenticated with the registry - but the pull secret is not in the bootc image - -**\--disable-selinux** - -: Disable SELinux in the target (installed) system. - - This is currently necessary to install \*from\* a system with - SELinux disabled but where the target does have SELinux enabled. - -**\--karg** *\* - -: Add a kernel argument. This option can be provided multiple times. - - Example: \--karg=nosmt \--karg=console=ttyS0,114800n8 - -**\--root-ssh-authorized-keys** *\* - -: The path to an \`authorized_keys\` that will be injected into the - \`root\` account. - - The implementation of this uses systemd \`tmpfiles.d\`, writing to a - file named \`/etc/tmpfiles.d/bootc-root-ssh.conf\`. This will have - the effect that by default, the SSH credentials will be set if not - present. The intention behind this is to allow mounting the whole - \`/root\` home directory as a \`tmpfs\`, while still getting the SSH - key replaced on boot. - -**\--generic-image** - -: Perform configuration changes suitable for a \"generic\" disk image. - At the moment: - - \- All bootloader types will be installed - Changes to the system - firmware will be skipped - -**\--bound-images** *\* \[default: stored\] - -: How should logically bound images be retrieved\ - - \ - *Possible values:* - - - stored: Bound images must exist in the source\'s root container - storage (default) - - - pull: Bound images will be pulled and stored directly in the - target\'s bootc container storage - -**\--stateroot** *\* - -: The stateroot name to use. Defaults to \`default\` - -**\--via-loopback** - -: Instead of targeting a block device, write to a file via loopback - -**-h**, **\--help** - -: Print help (see a summary with \'-h\') - -\<*DEVICE*\> - -: Target block device for installation. The entire device will be - wiped - -# VERSION - -v1.8.0 diff --git a/docs/src/man/bootc-install-to-existing-root.8.md b/docs/src/man/bootc-install-to-existing-root.8.md new file mode 100644 index 000000000..3b81f003d --- /dev/null +++ b/docs/src/man/bootc-install-to-existing-root.8.md @@ -0,0 +1,134 @@ +# NAME + +bootc-install-to-existing-root - Install to the host root filesystem + +# SYNOPSIS + +**bootc install to-existing-root** [*OPTIONS...*] [*ROOT_PATH*] + +# DESCRIPTION + +Install to the host root filesystem. + +This is a variant of `install to-filesystem` that is designed to +install \"alongside\" the running host root filesystem. Currently, the +host root filesystem\'s `/boot` partition will be wiped, but the +content of the existing root will otherwise be retained, and will need +to be cleaned up if desired when rebooted into the new root. + +# OPTIONS + + +**ROOT_PATH** + + Path to the mounted root; this is now not necessary to provide. Historically it was necessary to ensure the host rootfs was mounted at here via e.g. `-v /:/target` + +**--replace**=*REPLACE* + + Configure how existing data is treated + + Possible values: + - wipe + - alongside + + Default: alongside + +**--source-imgref**=*SOURCE_IMGREF* + + Install the system from an explicitly given source + +**--target-transport**=*TARGET_TRANSPORT* + + The transport; e.g. oci, oci-archive, containers-storage. Defaults to `registry` + + Default: registry + +**--target-imgref**=*TARGET_IMGREF* + + Specify the image to fetch for subsequent updates + +**--enforce-container-sigpolicy**=*ENFORCE_CONTAINER_SIGPOLICY* + + This is the inverse of the previous `--target-no-signature-verification` (which is now a no-op). Enabling this option enforces that `/etc/containers/policy.json` includes a default policy which requires signatures + + Possible values: + - true + - false + +**--run-fetch-check**=*RUN_FETCH_CHECK* + + Verify the image can be fetched from the bootc image. Updates may fail when the installation host is authenticated with the registry but the pull secret is not in the bootc image + + Possible values: + - true + - false + +**--skip-fetch-check**=*SKIP_FETCH_CHECK* + + Verify the image can be fetched from the bootc image. Updates may fail when the installation host is authenticated with the registry but the pull secret is not in the bootc image + + Possible values: + - true + - false + +**--disable-selinux**=*DISABLE_SELINUX* + + Disable SELinux in the target (installed) system + + Possible values: + - true + - false + +**--karg**=*KARG* + + Add a kernel argument. This option can be provided multiple times + +**--root-ssh-authorized-keys**=*ROOT_SSH_AUTHORIZED_KEYS* + + The path to an `authorized_keys` that will be injected into the `root` account + +**--generic-image**=*GENERIC_IMAGE* + + Perform configuration changes suitable for a "generic" disk image. At the moment: + + Possible values: + - true + - false + +**--bound-images**=*BOUND_IMAGES* + + How should logically bound images be retrieved + + Possible values: + - stored + - skip + - pull + + Default: stored + +**--stateroot**=*STATEROOT* + + The stateroot name to use. Defaults to `default` + +**--acknowledge-destructive**=*ACKNOWLEDGE_DESTRUCTIVE* + + Accept that this is a destructive action and skip a warning timer + + Possible values: + - true + - false + +**--cleanup**=*CLEANUP* + + Add the bootc-destructive-cleanup systemd service to delete files from the previous install on first boot + + Possible values: + - true + - false + + + +# VERSION + + + diff --git a/docs/src/man/bootc-install-to-existing-root.md b/docs/src/man/bootc-install-to-existing-root.md deleted file mode 100644 index 81596bdc4..000000000 --- a/docs/src/man/bootc-install-to-existing-root.md +++ /dev/null @@ -1,155 +0,0 @@ -# NAME - -bootc-install-to-existing-root - Install to the host root filesystem - -# SYNOPSIS - -**bootc install to-existing-root** \[**\--replace**\] -\[**\--source-imgref**\] \[**\--target-transport**\] -\[**\--target-imgref**\] \[**\--enforce-container-sigpolicy**\] -\[**\--run-fetch-check**\] \[**\--skip-fetch-check**\] -\[**\--disable-selinux**\] \[**\--karg**\] -\[**\--root-ssh-authorized-keys**\] \[**\--generic-image**\] -\[**\--bound-images**\] \[**\--stateroot**\] -\[**\--acknowledge-destructive**\] \[**\--cleanup**\] -\[**-h**\|**\--help**\] \[*ROOT_PATH*\] - -# DESCRIPTION - -Install to the host root filesystem. - -This is a variant of \`install to-filesystem\` that is designed to -install \"alongside\" the running host root filesystem. Currently, the -host root filesystem\'s \`/boot\` partition will be wiped, but the -content of the existing root will otherwise be retained, and will need -to be cleaned up if desired when rebooted into the new root. - -# OPTIONS - -**\--replace** *\* \[default: alongside\] - -: Configure how existing data is treated\ - - \ - *Possible values:* - - - wipe: Completely wipe the contents of the target filesystem. - This cannot be done if the target filesystem is the one the - system is booted from - - - alongside: This is a destructive operation in the sense that the - bootloader state will have its contents wiped and replaced. - However, the running system (and all files) will remain in place - until reboot - -**\--source-imgref** *\* - -: Install the system from an explicitly given source. - - By default, bootc install and install-to-filesystem assumes that it - runs in a podman container, and it takes the container image to - install from the podman\'s container registry. If \--source-imgref - is given, bootc uses it as the installation source, instead of the - behaviour explained in the previous paragraph. See skopeo(1) for - accepted formats. - -**\--target-transport** *\* \[default: registry\] - -: The transport; e.g. oci, oci-archive, containers-storage. Defaults - to \`registry\` - -**\--target-imgref** *\* - -: Specify the image to fetch for subsequent updates - -**\--enforce-container-sigpolicy** - -: This is the inverse of the previous - \`\--target-no-signature-verification\` (which is now a no-op). - Enabling this option enforces that \`/etc/containers/policy.json\` - includes a default policy which requires signatures - -**\--run-fetch-check** - -: Verify the image can be fetched from the bootc image. Updates may - fail when the installation host is authenticated with the registry - but the pull secret is not in the bootc image - -**\--skip-fetch-check** - -: Verify the image can be fetched from the bootc image. Updates may - fail when the installation host is authenticated with the registry - but the pull secret is not in the bootc image - -**\--disable-selinux** - -: Disable SELinux in the target (installed) system. - - This is currently necessary to install \*from\* a system with - SELinux disabled but where the target does have SELinux enabled. - -**\--karg** *\* - -: Add a kernel argument. This option can be provided multiple times. - - Example: \--karg=nosmt \--karg=console=ttyS0,114800n8 - -**\--root-ssh-authorized-keys** *\* - -: The path to an \`authorized_keys\` that will be injected into the - \`root\` account. - - The implementation of this uses systemd \`tmpfiles.d\`, writing to a - file named \`/etc/tmpfiles.d/bootc-root-ssh.conf\`. This will have - the effect that by default, the SSH credentials will be set if not - present. The intention behind this is to allow mounting the whole - \`/root\` home directory as a \`tmpfs\`, while still getting the SSH - key replaced on boot. - -**\--generic-image** - -: Perform configuration changes suitable for a \"generic\" disk image. - At the moment: - - \- All bootloader types will be installed - Changes to the system - firmware will be skipped - -**\--bound-images** *\* \[default: stored\] - -: How should logically bound images be retrieved\ - - \ - *Possible values:* - - - stored: Bound images must exist in the source\'s root container - storage (default) - - - pull: Bound images will be pulled and stored directly in the - target\'s bootc container storage - -**\--stateroot** *\* - -: The stateroot name to use. Defaults to \`default\` - -**\--acknowledge-destructive** - -: Accept that this is a destructive action and skip a warning timer - -**\--cleanup** - -: Add the bootc-destructive-cleanup systemd service to delete files - from the previous install on first boot - -**-h**, **\--help** - -: Print help (see a summary with \'-h\') - -\[*ROOT_PATH*\] \[default: /target\] - -: Path to the mounted root; this is now not necessary to provide. - Historically it was necessary to ensure the host rootfs was mounted - at here via e.g. \`-v /:/target\` - -# VERSION - -v1.8.0 diff --git a/docs/src/man/bootc-install-to-filesystem.8.md b/docs/src/man/bootc-install-to-filesystem.8.md new file mode 100644 index 000000000..1f9a33f43 --- /dev/null +++ b/docs/src/man/bootc-install-to-filesystem.8.md @@ -0,0 +1,142 @@ +# NAME + +bootc-install-to-filesystem - Install to an externally created +filesystem structure + +# SYNOPSIS + +**bootc install to-filesystem** [*OPTIONS...*] <*ROOT_PATH*> + +# DESCRIPTION + +Install to an externally created filesystem structure. + +In this variant of installation, the root filesystem alongside any +necessary platform partitions (such as the EFI system partition) are +prepared and mounted by an external tool or script. The root filesystem +is currently expected to be empty by default. + +# OPTIONS + + +**ROOT_PATH** + + Path to the mounted root filesystem + + This argument is required. + +**--root-mount-spec**=*ROOT_MOUNT_SPEC* + + Source device specification for the root filesystem. For example, UUID=2e9f4241-229b-4202-8429-62d2302382e1 + +**--boot-mount-spec**=*BOOT_MOUNT_SPEC* + + Mount specification for the /boot filesystem + +**--replace**=*REPLACE* + + Initialize the system in-place; at the moment, only one mode for this is implemented. In the future, it may also be supported to set up an explicit "dual boot" system + + Possible values: + - wipe + - alongside + +**--acknowledge-destructive**=*ACKNOWLEDGE_DESTRUCTIVE* + + If the target is the running system's root filesystem, this will skip any warnings + + Possible values: + - true + - false + +**--skip-finalize**=*SKIP_FINALIZE* + + The default mode is to "finalize" the target filesystem by invoking `fstrim` and similar operations, and finally mounting it readonly. This option skips those operations. It is then the responsibility of the invoking code to perform those operations + + Possible values: + - true + - false + +**--source-imgref**=*SOURCE_IMGREF* + + Install the system from an explicitly given source + +**--target-transport**=*TARGET_TRANSPORT* + + The transport; e.g. oci, oci-archive, containers-storage. Defaults to `registry` + + Default: registry + +**--target-imgref**=*TARGET_IMGREF* + + Specify the image to fetch for subsequent updates + +**--enforce-container-sigpolicy**=*ENFORCE_CONTAINER_SIGPOLICY* + + This is the inverse of the previous `--target-no-signature-verification` (which is now a no-op). Enabling this option enforces that `/etc/containers/policy.json` includes a default policy which requires signatures + + Possible values: + - true + - false + +**--run-fetch-check**=*RUN_FETCH_CHECK* + + Verify the image can be fetched from the bootc image. Updates may fail when the installation host is authenticated with the registry but the pull secret is not in the bootc image + + Possible values: + - true + - false + +**--skip-fetch-check**=*SKIP_FETCH_CHECK* + + Verify the image can be fetched from the bootc image. Updates may fail when the installation host is authenticated with the registry but the pull secret is not in the bootc image + + Possible values: + - true + - false + +**--disable-selinux**=*DISABLE_SELINUX* + + Disable SELinux in the target (installed) system + + Possible values: + - true + - false + +**--karg**=*KARG* + + Add a kernel argument. This option can be provided multiple times + +**--root-ssh-authorized-keys**=*ROOT_SSH_AUTHORIZED_KEYS* + + The path to an `authorized_keys` that will be injected into the `root` account + +**--generic-image**=*GENERIC_IMAGE* + + Perform configuration changes suitable for a "generic" disk image. At the moment: + + Possible values: + - true + - false + +**--bound-images**=*BOUND_IMAGES* + + How should logically bound images be retrieved + + Possible values: + - stored + - skip + - pull + + Default: stored + +**--stateroot**=*STATEROOT* + + The stateroot name to use. Defaults to `default` + + + +# VERSION + + + diff --git a/docs/src/man/bootc-install-to-filesystem.md b/docs/src/man/bootc-install-to-filesystem.md deleted file mode 100644 index 8c918da69..000000000 --- a/docs/src/man/bootc-install-to-filesystem.md +++ /dev/null @@ -1,176 +0,0 @@ -# NAME - -bootc-install-to-filesystem - Install to an externally created -filesystem structure - -# SYNOPSIS - -**bootc install to-filesystem** \[**\--root-mount-spec**\] -\[**\--boot-mount-spec**\] \[**\--replace**\] -\[**\--acknowledge-destructive**\] \[**\--skip-finalize**\] -\[**\--source-imgref**\] \[**\--target-transport**\] -\[**\--target-imgref**\] \[**\--enforce-container-sigpolicy**\] -\[**\--run-fetch-check**\] \[**\--skip-fetch-check**\] -\[**\--disable-selinux**\] \[**\--karg**\] -\[**\--root-ssh-authorized-keys**\] \[**\--generic-image**\] -\[**\--bound-images**\] \[**\--stateroot**\] \[**-h**\|**\--help**\] -\<*ROOT_PATH*\> - -# DESCRIPTION - -Install to an externally created filesystem structure. - -In this variant of installation, the root filesystem alongside any -necessary platform partitions (such as the EFI system partition) are -prepared and mounted by an external tool or script. The root filesystem -is currently expected to be empty by default. - -# OPTIONS - -**\--root-mount-spec** *\* - -: Source device specification for the root filesystem. For example, - UUID=2e9f4241-229b-4202-8429-62d2302382e1 - - If not provided, the UUID of the target filesystem will be used. - -**\--boot-mount-spec** *\* - -: Mount specification for the /boot filesystem. - - This is optional. If \`/boot\` is detected as a mounted partition, - then its UUID will be used. - -**\--replace** *\* - -: Initialize the system in-place; at the moment, only one mode for - this is implemented. In the future, it may also be supported to set - up an explicit \"dual boot\" system\ - - \ - *Possible values:* - - - wipe: Completely wipe the contents of the target filesystem. - This cannot be done if the target filesystem is the one the - system is booted from - - - alongside: This is a destructive operation in the sense that the - bootloader state will have its contents wiped and replaced. - However, the running system (and all files) will remain in place - until reboot - -**\--acknowledge-destructive** - -: If the target is the running system\'s root filesystem, this will - skip any warnings - -**\--skip-finalize** - -: The default mode is to \"finalize\" the target filesystem by - invoking \`fstrim\` and similar operations, and finally mounting it - readonly. This option skips those operations. It is then the - responsibility of the invoking code to perform those operations - -**\--source-imgref** *\* - -: Install the system from an explicitly given source. - - By default, bootc install and install-to-filesystem assumes that it - runs in a podman container, and it takes the container image to - install from the podman\'s container registry. If \--source-imgref - is given, bootc uses it as the installation source, instead of the - behaviour explained in the previous paragraph. See skopeo(1) for - accepted formats. - -**\--target-transport** *\* \[default: registry\] - -: The transport; e.g. oci, oci-archive, containers-storage. Defaults - to \`registry\` - -**\--target-imgref** *\* - -: Specify the image to fetch for subsequent updates - -**\--enforce-container-sigpolicy** - -: This is the inverse of the previous - \`\--target-no-signature-verification\` (which is now a no-op). - Enabling this option enforces that \`/etc/containers/policy.json\` - includes a default policy which requires signatures - -**\--run-fetch-check** - -: Verify the image can be fetched from the bootc image. Updates may - fail when the installation host is authenticated with the registry - but the pull secret is not in the bootc image - -**\--skip-fetch-check** - -: Verify the image can be fetched from the bootc image. Updates may - fail when the installation host is authenticated with the registry - but the pull secret is not in the bootc image - -**\--disable-selinux** - -: Disable SELinux in the target (installed) system. - - This is currently necessary to install \*from\* a system with - SELinux disabled but where the target does have SELinux enabled. - -**\--karg** *\* - -: Add a kernel argument. This option can be provided multiple times. - - Example: \--karg=nosmt \--karg=console=ttyS0,114800n8 - -**\--root-ssh-authorized-keys** *\* - -: The path to an \`authorized_keys\` that will be injected into the - \`root\` account. - - The implementation of this uses systemd \`tmpfiles.d\`, writing to a - file named \`/etc/tmpfiles.d/bootc-root-ssh.conf\`. This will have - the effect that by default, the SSH credentials will be set if not - present. The intention behind this is to allow mounting the whole - \`/root\` home directory as a \`tmpfs\`, while still getting the SSH - key replaced on boot. - -**\--generic-image** - -: Perform configuration changes suitable for a \"generic\" disk image. - At the moment: - - \- All bootloader types will be installed - Changes to the system - firmware will be skipped - -**\--bound-images** *\* \[default: stored\] - -: How should logically bound images be retrieved\ - - \ - *Possible values:* - - - stored: Bound images must exist in the source\'s root container - storage (default) - - - pull: Bound images will be pulled and stored directly in the - target\'s bootc container storage - -**\--stateroot** *\* - -: The stateroot name to use. Defaults to \`default\` - -**-h**, **\--help** - -: Print help (see a summary with \'-h\') - -\<*ROOT_PATH*\> - -: Path to the mounted root filesystem. - - By default, the filesystem UUID will be discovered and used for - mounting. To override this, use \`\--root-mount-spec\`. - -# VERSION - -v1.8.0 diff --git a/docs/src/man/bootc-install.8.md b/docs/src/man/bootc-install.8.md new file mode 100644 index 000000000..aaea2c6a4 --- /dev/null +++ b/docs/src/man/bootc-install.8.md @@ -0,0 +1,51 @@ +# NAME + +bootc-install - Install the running container to a target + +# SYNOPSIS + +**bootc install** [*OPTIONS...*] <*SUBCOMMAND*> + +# DESCRIPTION + +Install the running container to a target. + +## Understanding installations + +OCI containers are effectively layers of tarballs with JSON for +metadata; they cannot be booted directly. The `bootc install` flow is +a highly opinionated method to take the contents of the container image +and install it to a target block device (or an existing filesystem) in +such a way that it can be booted. + +For example, a Linux partition table and filesystem is used, and the +bootloader and kernel embedded in the container image are also prepared. + +A bootc installed container currently uses OSTree as a backend, and this +sets it up such that a subsequent `bootc upgrade` can perform in-place +updates. + +An installation is not simply a copy of the container filesystem, but +includes other setup and metadata. + + + + +# SUBCOMMANDS + + +| Command | Description | +|---------|-------------| +| **bootc install to-disk** | Install to the target block device | +| **bootc install to-filesystem** | Install to an externally created filesystem structure | +| **bootc install to-existing-root** | Install to the host root filesystem | +| **bootc install finalize** | Execute this as the penultimate step of an installation using `install to-filesystem` | +| **bootc install ensure-completion** | Intended for use in environments that are performing an ostree-based installation, not bootc | +| **bootc install print-configuration** | Output JSON to stdout that contains the merged installation configuration as it may be relevant to calling processes using `install to-filesystem` that in particular want to discover the desired root filesystem type from the container image | + + + +# VERSION + + + diff --git a/docs/src/man/bootc-install.md b/docs/src/man/bootc-install.md deleted file mode 100644 index 47bc6d230..000000000 --- a/docs/src/man/bootc-install.md +++ /dev/null @@ -1,74 +0,0 @@ -# NAME - -bootc-install - Install the running container to a target - -# SYNOPSIS - -**bootc install** \[**-h**\|**\--help**\] \<*subcommands*\> - -# DESCRIPTION - -Install the running container to a target. - -\## Understanding installations - -OCI containers are effectively layers of tarballs with JSON for -metadata; they cannot be booted directly. The \`bootc install\` flow is -a highly opinionated method to take the contents of the container image -and install it to a target block device (or an existing filesystem) in -such a way that it can be booted. - -For example, a Linux partition table and filesystem is used, and the -bootloader and kernel embedded in the container image are also prepared. - -A bootc installed container currently uses OSTree as a backend, and this -sets it up such that a subsequent \`bootc upgrade\` can perform in-place -updates. - -An installation is not simply a copy of the container filesystem, but -includes other setup and metadata. - -# OPTIONS - -**-h**, **\--help** - -: Print help (see a summary with \'-h\') - -# SUBCOMMANDS - -bootc-install-to-disk(8) - -: Install to the target block device - -bootc-install-to-filesystem(8) - -: Install to an externally created filesystem structure - -bootc-install-to-existing-root(8) - -: Install to the host root filesystem - -bootc-install-finalize(8) - -: Execute this as the penultimate step of an installation using - \`install to-filesystem\` - -bootc-install-ensure-completion(8) - -: Intended for use in environments that are performing an ostree-based - installation, not bootc - -bootc-install-print-configuration(8) - -: Output JSON to stdout that contains the merged installation - configuration as it may be relevant to calling processes using - \`install to-filesystem\` that in particular want to discover the - desired root filesystem type from the container image - -bootc-install-help(8) - -: Print this message or the help of the given subcommand(s) - -# VERSION - -v1.8.0 diff --git a/docs/src/man/bootc-rollback.8.md b/docs/src/man/bootc-rollback.8.md new file mode 100644 index 000000000..bc6215a19 --- /dev/null +++ b/docs/src/man/bootc-rollback.8.md @@ -0,0 +1,75 @@ +# NAME + +bootc-rollback - Change the bootloader entry ordering + +# SYNOPSIS + +**bootc rollback** [*OPTIONS...*] + +# DESCRIPTION + +Change the bootloader entry ordering; the deployment under `rollback` will be queued for the next boot, +and the current will become rollback. If there is a `staged` entry (an unapplied, queued upgrade) +then it will be discarded. + +Note that absent any additional control logic, if there is an active agent doing automated upgrades +(such as the default `bootc-fetch-apply-updates.timer` and associated `.service`) the +change here may be reverted. It's recommended to only use this in concert with an agent that +is in active control. + +A systemd journal message will be logged with `MESSAGE_ID=26f3b1eb24464d12aa5e7b544a6b5468` in +order to detect a rollback invocation. + +## Note on Rollbacks and the `/etc` Directory + +When you perform a rollback (e.g., with `bootc rollback`), any +changes made to files in the `/etc` directory won't carry over +to the rolled-back deployment. The `/etc` files will revert +to their state from that previous deployment instead. + +This is because `bootc rollback` just reorders the existing +deployments. It doesn't create new deployments. The `/etc` +merges happen when new deployments are created. + +# OPTIONS + + +**--apply**=*APPLY* + + Restart or reboot into the rollback image + + Possible values: + - true + - false + +**--soft-reboot**=*SOFT_REBOOT* + + Configure soft reboot behavior + + Possible values: + - required + - auto + + + +# EXAMPLES + +Rollback to the previous deployment: + + bootc rollback + +Rollback and immediately apply the changes: + + bootc rollback --apply + +Rollback with soft reboot if possible: + + bootc rollback --apply --soft-reboot=auto + +# SEE ALSO + +**bootc**(8), **bootc-upgrade**(8), **bootc-switch**(8), **bootc-status**(8) + +# VERSION + + diff --git a/docs/src/man/bootc-rollback.md b/docs/src/man/bootc-rollback.md deleted file mode 100644 index 7ec395c68..000000000 --- a/docs/src/man/bootc-rollback.md +++ /dev/null @@ -1,75 +0,0 @@ -# NAME - -bootc-rollback - Change the bootloader entry ordering; the deployment -under \`rollback\` will be queued for the next boot, and the current -will become rollback. If there is a \`staged\` entry (an unapplied, -queued upgrade) then it will be discarded - -# SYNOPSIS - -**bootc rollback** \[**\--apply**\] \[**\--soft-reboot**\] -\[**-h**\|**\--help**\] - -# DESCRIPTION - -Change the bootloader entry ordering; the deployment under \`rollback\` -will be queued for the next boot, and the current will become rollback. -If there is a \`staged\` entry (an unapplied, queued upgrade) then it -will be discarded. - -Note that absent any additional control logic, if there is an active -agent doing automated upgrades (such as the default -\`bootc-fetch-apply-updates.timer\` and associated \`.service\`) the -change here may be reverted. It\'s recommended to only use this in -concert with an agent that is in active control. - -A systemd journal message will be logged with -\`MESSAGE_ID=26f3b1eb24464d12aa5e7b544a6b5468\` in order to detect a -rollback invocation. - -# OPTIONS - -**\--apply** - -: Restart or reboot into the rollback image. - - Currently, this option always reboots. In the future this command - will detect the case where no kernel changes are queued, and perform - a userspace-only restart. - -**\--soft-reboot** *\* - -: Configure soft reboot behavior. - - \'required\' will fail if soft reboot is not available. \'auto\' - will use soft reboot if available, otherwise fall back to regular - reboot.\ - - \ - *Possible values:* - - - required: Require a soft reboot; fail if not possible - - - auto: Automatically use soft reboot if possible, otherwise use - regular reboot - -**-h**, **\--help** - -: Print help (see a summary with \'-h\') - -# EXTRA - -Note on Rollbacks and the \`/etc\` Directory: - -When you perform a rollback (e.g., with \`bootc rollback\`), any changes -made to files in the \`/etc\` directory won\'t carry over to the -rolled-back deployment. The \`/etc\` files will revert to their state -from that previous deployment instead. - -This is because \`bootc rollback\` just reorders the existing -deployments. It doesn\'t create new deployments. The \`/etc\` merges -happen when new deployments are created. - -# VERSION - -v1.8.0 diff --git a/docs/src/man-md/bootc-status-updated.path.md b/docs/src/man/bootc-status-updated.path.5.md similarity index 91% rename from docs/src/man-md/bootc-status-updated.path.md rename to docs/src/man/bootc-status-updated.path.5.md index d05831e00..5fc8ffe79 100644 --- a/docs/src/man-md/bootc-status-updated.path.md +++ b/docs/src/man/bootc-status-updated.path.5.md @@ -17,3 +17,7 @@ update/upgrade/edit/switch/rollback operation. # SEE ALSO **bootc**(1), **bootc-status-updated.target**(8) + +# VERSION + + diff --git a/docs/src/man-md/bootc-status-updated.target.md b/docs/src/man/bootc-status-updated.target.5.md similarity index 92% rename from docs/src/man-md/bootc-status-updated.target.md rename to docs/src/man/bootc-status-updated.target.5.md index cef1c976b..4f5e0fccf 100644 --- a/docs/src/man-md/bootc-status-updated.target.md +++ b/docs/src/man/bootc-status-updated.target.5.md @@ -21,3 +21,7 @@ WantedBy=bootc-status-updated.target # SEE ALSO **bootc**(1), **bootc-status-updated.path**(8) + +# VERSION + + diff --git a/docs/src/man/bootc-status.8.md b/docs/src/man/bootc-status.8.md new file mode 100644 index 000000000..efc7e958d --- /dev/null +++ b/docs/src/man/bootc-status.8.md @@ -0,0 +1,85 @@ +# NAME + +bootc-status - Display status + +# SYNOPSIS + +**bootc status** [*OPTIONS...*] + +# DESCRIPTION + +Display status. + +If standard output is a terminal, this will output a description of the bootc system state. +If standard output is not a terminal, output a YAML-formatted object using a schema +intended to match a Kubernetes resource that describes the state of the booted system. + +## Parsing output via programs + +Either the default YAML format or `--format=json` can be used. Do not attempt to +explicitly parse the output of `--format=humanreadable` as it will very likely +change over time. + +## Programmatically detecting whether the system is deployed via bootc + +Invoke e.g. `bootc status --json`, and check if `status.booted` is not `null`. + +# OPTIONS + + +**--format**=*FORMAT* + + The output format + + Possible values: + - humanreadable + - yaml + - json + +**--format-version**=*FORMAT_VERSION* + + The desired format version. There is currently one supported version, which is exposed as both `0` and `1`. Pass this option to explicitly request it; it is possible that another future version 2 or newer will be supported in the future + +**--booted**=*BOOTED* + + Only display status for the booted deployment + + Possible values: + - true + - false + +**-v**, **--verbose**=*VERBOSE* + + Include additional fields in human readable format + + Possible values: + - true + - false + + + +# EXAMPLES + +Show current system status: + + bootc status + +Show status in JSON format: + + bootc status --format=json + +Show detailed status with verbose output: + + bootc status --verbose + +Show only booted deployment status: + + bootc status --booted + +# SEE ALSO + +**bootc**(8), **bootc-upgrade**(8), **bootc-switch**(8), **bootc-rollback**(8) + +# VERSION + + diff --git a/docs/src/man/bootc-status.md b/docs/src/man/bootc-status.md deleted file mode 100644 index e001afb3a..000000000 --- a/docs/src/man/bootc-status.md +++ /dev/null @@ -1,66 +0,0 @@ -# NAME - -bootc-status - Display status - -# SYNOPSIS - -**bootc status** \[**\--format**\] \[**\--format-version**\] -\[**\--booted**\] \[**-v**\|**\--verbose**\] \[**-h**\|**\--help**\] - -# DESCRIPTION - -Display status - -If standard output is a terminal, this will output a description of the -bootc system state. If standard output is not a terminal, output a -YAML-formatted object using a schema intended to match a Kubernetes -resource that describes the state of the booted system. - -\## Parsing output via programs - -Either the default YAML format or \`\--format=json\` can be used. Do not -attempt to explicitly parse the output of \`\--format=humanreadable\` as -it will very likely change over time. - -\## Programmatically detecting whether the system is deployed via bootc - -Invoke e.g. \`bootc status \--json\`, and check if \`status.booted\` is -not \`null\`. - -# OPTIONS - -**\--format** *\* - -: The output format\ - - \ - *Possible values:* - - - humanreadable: Output in Human Readable format - - - yaml: Output in YAML format - - - json: Output in JSON format - -**\--format-version** *\* - -: The desired format version. There is currently one supported - version, which is exposed as both \`0\` and \`1\`. Pass this option - to explicitly request it; it is possible that another future version - 2 or newer will be supported in the future - -**\--booted** - -: Only display status for the booted deployment - -**-v**, **\--verbose** - -: Include additional fields in human readable format - -**-h**, **\--help** - -: Print help (see a summary with \'-h\') - -# VERSION - -v1.8.0 diff --git a/docs/src/man/bootc-switch.8.md b/docs/src/man/bootc-switch.8.md new file mode 100644 index 000000000..1d504c90c --- /dev/null +++ b/docs/src/man/bootc-switch.8.md @@ -0,0 +1,112 @@ +# NAME + +bootc-switch - Target a new container image reference to boot + +# SYNOPSIS + +**bootc switch** [*OPTIONS...*] <*TARGET*> + +# DESCRIPTION + +Target a new container image reference to boot. + +This is almost exactly the same operation as `upgrade`, but additionally changes the container image reference +instead. + +## Usage + +A common pattern is to have a management agent control operating system updates via container image tags; +for example, `quay.io/exampleos/someuser:v1.0` and `quay.io/exampleos/someuser:v1.1` where some machines +are tracking `:v1.0`, and as a rollout progresses, machines can be switched to `v:1.1`. + +## Applying Changes + +The `--apply` option will automatically take action (rebooting) if the system has changed after switching to the new image. Currently, this option always reboots the system. In the future, this command may detect cases where no kernel changes are queued and perform a userspace-only restart instead. + +## Soft Reboot + +The `--soft-reboot` option configures soft reboot behavior when used with `--apply`: + +- `required`: The operation will fail if soft reboot is not available on the target system +- `auto`: Uses soft reboot if available on the target system, otherwise falls back to a regular reboot + +Soft reboot allows faster system restart by avoiding full hardware reboot when possible. + +# OPTIONS + + +**TARGET** + + Target image to use for the next boot + + This argument is required. + +**--quiet**=*QUIET* + + Don't display progress + + Possible values: + - true + - false + +**--apply**=*APPLY* + + Restart or reboot into the new target image + + Possible values: + - true + - false + +**--soft-reboot**=*SOFT_REBOOT* + + Configure soft reboot behavior + + Possible values: + - required + - auto + +**--transport**=*TRANSPORT* + + The transport; e.g. oci, oci-archive, containers-storage. Defaults to `registry` + + Default: registry + +**--enforce-container-sigpolicy**=*ENFORCE_CONTAINER_SIGPOLICY* + + This is the inverse of the previous `--target-no-signature-verification` (which is now a no-op) + + Possible values: + - true + - false + +**--retain**=*RETAIN* + + Retain reference to currently booted image + + Possible values: + - true + - false + + + +# EXAMPLES + +Switch to a different image version: + + bootc switch quay.io/exampleos/myapp:v1.1 + +Switch and immediately apply the changes: + + bootc switch --apply quay.io/exampleos/myapp:v1.1 + +Switch with soft reboot if possible: + + bootc switch --apply --soft-reboot=auto quay.io/exampleos/myapp:v1.1 + +# SEE ALSO + +**bootc**(8), **bootc-upgrade**(8), **bootc-status**(8), **bootc-rollback**(8) + +# VERSION + + diff --git a/docs/src/man/bootc-switch.md b/docs/src/man/bootc-switch.md deleted file mode 100644 index 183607c78..000000000 --- a/docs/src/man/bootc-switch.md +++ /dev/null @@ -1,85 +0,0 @@ -# NAME - -bootc-switch - Target a new container image reference to boot - -# SYNOPSIS - -**bootc switch** \[**\--quiet**\] \[**\--apply**\] -\[**\--soft-reboot**\] \[**\--transport**\] -\[**\--enforce-container-sigpolicy**\] \[**\--retain**\] -\[**-h**\|**\--help**\] \<*TARGET*\> - -# DESCRIPTION - -Target a new container image reference to boot. - -This is almost exactly the same operation as \`upgrade\`, but -additionally changes the container image reference instead. - -\## Usage - -A common pattern is to have a management agent control operating system -updates via container image tags; for example, -\`quay.io/exampleos/someuser:v1.0\` and -\`quay.io/exampleos/someuser:v1.1\` where some machines are tracking -\`:v1.0\`, and as a rollout progresses, machines can be switched to -\`v:1.1\`. - -# OPTIONS - -**\--quiet** - -: Don\'t display progress - -**\--apply** - -: Restart or reboot into the new target image. - - Currently, this option always reboots. In the future this command - will detect the case where no kernel changes are queued, and perform - a userspace-only restart. - -**\--soft-reboot** *\* - -: Configure soft reboot behavior. - - \'required\' will fail if soft reboot is not available. \'auto\' - will use soft reboot if available, otherwise fall back to regular - reboot.\ - - \ - *Possible values:* - - - required: Require a soft reboot; fail if not possible - - - auto: Automatically use soft reboot if possible, otherwise use - regular reboot - -**\--transport** *\* \[default: registry\] - -: The transport; e.g. oci, oci-archive, containers-storage. Defaults - to \`registry\` - -**\--enforce-container-sigpolicy** - -: This is the inverse of the previous - \`\--target-no-signature-verification\` (which is now a no-op). - - Enabling this option enforces that \`/etc/containers/policy.json\` - includes a default policy which requires signatures. - -**\--retain** - -: Retain reference to currently booted image - -**-h**, **\--help** - -: Print help (see a summary with \'-h\') - -\<*TARGET*\> - -: Target image to use for the next boot - -# VERSION - -v1.8.0 diff --git a/docs/src/man/bootc-upgrade.8.md b/docs/src/man/bootc-upgrade.8.md new file mode 100644 index 000000000..a0c611a8a --- /dev/null +++ b/docs/src/man/bootc-upgrade.8.md @@ -0,0 +1,98 @@ +# NAME + +bootc-upgrade - Download and queue an updated container image to apply + +# SYNOPSIS + +**bootc upgrade** [*OPTIONS...*] + +# DESCRIPTION + +Download and queue an updated container image to apply. + +This does not affect the running system; updates operate in an "A/B" style by default. + +A queued update is visible as `staged` in `bootc status`. + +## Checking for Updates + +The `--check` option allows you to verify if updates are available without downloading the full image layers. This only downloads the updated manifest and image configuration (typically kilobyte-sized metadata), making it much faster than a full upgrade. + +## Applying Updates + +Currently by default, the update will be applied at shutdown time via `ostree-finalize-staged.service`. +There is also an explicit `bootc upgrade --apply` verb which will automatically take action (rebooting) +if the system has changed. + +The `--apply` option currently always reboots the system. In the future, this command may detect cases where no kernel changes are queued and perform a userspace-only restart instead. + +However, in the future this is likely to change such that reboots outside of a `bootc upgrade --apply` +do *not* automatically apply the update in addition. + +## Soft Reboot + +The `--soft-reboot` option configures soft reboot behavior when used with `--apply`: + +- `required`: The operation will fail if soft reboot is not available on the target system +- `auto`: Uses soft reboot if available on the target system, otherwise falls back to a regular reboot + +Soft reboot allows faster system restart by avoiding full hardware reboot when possible. + +# OPTIONS + + +**--quiet**=*QUIET* + + Don't display progress + + Possible values: + - true + - false + +**--check**=*CHECK* + + Check if an update is available without applying it + + Possible values: + - true + - false + +**--apply**=*APPLY* + + Restart or reboot into the new target image + + Possible values: + - true + - false + +**--soft-reboot**=*SOFT_REBOOT* + + Configure soft reboot behavior + + Possible values: + - required + - auto + + + +# EXAMPLES + +Check for available updates: + + bootc upgrade --check + +Upgrade and immediately apply the changes: + + bootc upgrade --apply + +Upgrade with soft reboot if possible: + + bootc upgrade --apply --soft-reboot=auto + +# SEE ALSO + +**bootc**(8), **bootc-switch**(8), **bootc-status**(8), **bootc-rollback**(8) + +# VERSION + + diff --git a/docs/src/man/bootc-upgrade.md b/docs/src/man/bootc-upgrade.md deleted file mode 100644 index 492350685..000000000 --- a/docs/src/man/bootc-upgrade.md +++ /dev/null @@ -1,72 +0,0 @@ -# NAME - -bootc-upgrade - Download and queue an updated container image to apply - -# SYNOPSIS - -**bootc upgrade** \[**\--quiet**\] \[**\--check**\] \[**\--apply**\] -\[**\--soft-reboot**\] \[**-h**\|**\--help**\] - -# DESCRIPTION - -Download and queue an updated container image to apply. - -This does not affect the running system; updates operate in an \"A/B\" -style by default. - -A queued update is visible as \`staged\` in \`bootc status\`. - -Currently by default, the update will be applied at shutdown time via -\`ostree-finalize-staged.service\`. There is also an explicit \`bootc -upgrade \--apply\` verb which will automatically take action (rebooting) -if the system has changed. - -However, in the future this is likely to change such that reboots -outside of a \`bootc upgrade \--apply\` do \*not\* automatically apply -the update in addition. - -# OPTIONS - -**\--quiet** - -: Don\'t display progress - -**\--check** - -: Check if an update is available without applying it. - - This only downloads an updated manifest and image configuration - (i.e. typically kilobyte-sized metadata) as opposed to the image - layers. - -**\--apply** - -: Restart or reboot into the new target image. - - Currently, this option always reboots. In the future this command - will detect the case where no kernel changes are queued, and perform - a userspace-only restart. - -**\--soft-reboot** *\* - -: Configure soft reboot behavior. - - \'required\' will fail if soft reboot is not available. \'auto\' - will use soft reboot if available, otherwise fall back to regular - reboot.\ - - \ - *Possible values:* - - - required: Require a soft reboot; fail if not possible - - - auto: Automatically use soft reboot if possible, otherwise use - regular reboot - -**-h**, **\--help** - -: Print help (see a summary with \'-h\') - -# VERSION - -v1.8.0 diff --git a/docs/src/man/bootc-usr-overlay.8.md b/docs/src/man/bootc-usr-overlay.8.md new file mode 100644 index 000000000..fb3b9d632 --- /dev/null +++ b/docs/src/man/bootc-usr-overlay.8.md @@ -0,0 +1,40 @@ +# NAME + +bootc-usr-overlay - Adds a transient writable overlayfs on `/usr` that +will be discarded on reboot + +# SYNOPSIS + +**bootc usr-overlay** [*OPTIONS...*] + +# DESCRIPTION + +Adds a transient writable overlayfs on `/usr` that will be discarded +on reboot. + +## USE CASES + +A common pattern is wanting to use tracing/debugging tools, such as +`strace` that may not be in the base image. A system package manager +such as `apt` or `dnf` can apply changes into this transient overlay +that will be discarded on reboot. + +## /ETC AND /VAR + +However, this command has no effect on `/etc` and `/var` - changes +written there will persist. It is common for package installations to +modify these directories. + +## UNMOUNTING + +Almost always, a system process will hold a reference to the open mount +point. You can however invoke `umount -l /usr` to perform a "lazy +unmount". + + + + +# VERSION + + + diff --git a/docs/src/man/bootc-usr-overlay.md b/docs/src/man/bootc-usr-overlay.md deleted file mode 100644 index 1718eafca..000000000 --- a/docs/src/man/bootc-usr-overlay.md +++ /dev/null @@ -1,42 +0,0 @@ -# NAME - -bootc-usr-overlay - Adds a transient writable overlayfs on \`/usr\` that -will be discarded on reboot - -# SYNOPSIS - -**bootc usr-overlay** \[**-h**\|**\--help**\] - -# DESCRIPTION - -Adds a transient writable overlayfs on \`/usr\` that will be discarded -on reboot. - -\## Use cases - -A common pattern is wanting to use tracing/debugging tools, such as -\`strace\` that may not be in the base image. A system package manager -such as \`apt\` or \`dnf\` can apply changes into this transient overlay -that will be discarded on reboot. - -\## /etc and /var - -However, this command has no effect on \`/etc\` and \`/var\` - changes -written there will persist. It is common for package installations to -modify these directories. - -\## Unmounting - -Almost always, a system process will hold a reference to the open mount -point. You can however invoke \`umount -l /usr\` to perform a \"lazy -unmount\". - -# OPTIONS - -**-h**, **\--help** - -: Print help (see a summary with \'-h\') - -# VERSION - -v1.8.0 diff --git a/docs/src/man/bootc.8.md b/docs/src/man/bootc.8.md new file mode 100644 index 000000000..cc3b3b48d --- /dev/null +++ b/docs/src/man/bootc.8.md @@ -0,0 +1,42 @@ +# NAME + +bootc - Deploy and transactionally in-place with bootable container +images + +# SYNOPSIS + +**bootc** [*OPTIONS...*] <*SUBCOMMAND*> + +# DESCRIPTION + +Deploy and transactionally in-place with bootable container images. + +The `bootc` project currently uses ostree-containers as a backend to +support a model of bootable container images. Once installed, whether +directly via `bootc install` (executed as part of a container) or via +another mechanism such as an OS installer tool, further updates can be +pulled and `bootc upgrade`. + + + + +# SUBCOMMANDS + + +| Command | Description | +|---------|-------------| +| **bootc upgrade** | Download and queue an updated container image to apply | +| **bootc switch** | Target a new container image reference to boot | +| **bootc rollback** | Change the bootloader entry ordering; the deployment under `rollback` will be queued for the next boot, and the current will become rollback. If there is a `staged` entry (an unapplied, queued upgrade) then it will be discarded | +| **bootc edit** | Apply full changes to the host specification | +| **bootc status** | Display status | +| **bootc usr-overlay** | Add a transient writable overlayfs on `/usr` | +| **bootc install** | Install the running container to a target | +| **bootc container** | Operations which can be executed as part of a container build | + + + +# VERSION + + + diff --git a/docs/src/man/bootc.md b/docs/src/man/bootc.md deleted file mode 100644 index 076ecaf59..000000000 --- a/docs/src/man/bootc.md +++ /dev/null @@ -1,75 +0,0 @@ -# NAME - -bootc - Deploy and transactionally in-place with bootable container -images - -# SYNOPSIS - -**bootc** \[**-h**\|**\--help**\] \[**-V**\|**\--version**\] -\<*subcommands*\> - -# DESCRIPTION - -Deploy and transactionally in-place with bootable container images. - -The \`bootc\` project currently uses ostree-containers as a backend to -support a model of bootable container images. Once installed, whether -directly via \`bootc install\` (executed as part of a container) or via -another mechanism such as an OS installer tool, further updates can be -pulled and \`bootc upgrade\`. - -# OPTIONS - -**-h**, **\--help** - -: Print help (see a summary with \'-h\') - -**-V**, **\--version** - -: Print version - -# SUBCOMMANDS - -bootc-upgrade(8) - -: Download and queue an updated container image to apply - -bootc-switch(8) - -: Target a new container image reference to boot - -bootc-rollback(8) - -: Change the bootloader entry ordering; the deployment under - \`rollback\` will be queued for the next boot, and the current will - become rollback. If there is a \`staged\` entry (an unapplied, - queued upgrade) then it will be discarded - -bootc-edit(8) - -: Apply full changes to the host specification - -bootc-status(8) - -: Display status - -bootc-usr-overlay(8) - -: Adds a transient writable overlayfs on \`/usr\` that will be - discarded on reboot - -bootc-install(8) - -: Install the running container to a target - -bootc-container(8) - -: Operations which can be executed as part of a container build - -bootc-help(8) - -: Print this message or the help of the given subcommand(s) - -# VERSION - -v1.8.0