diff --git a/.gitignore b/.gitignore index 1b37bfd49..15cc9658d 100644 --- a/.gitignore +++ b/.gitignore @@ -212,5 +212,9 @@ rfc.md *.tar.xz *.tar.bz2 +# Snap build artifacts +*.snap +*.comp + # Markdown/mermaid lint tooling deps scripts/lint-mermaid/node_modules/ diff --git a/deploy/snap/README.md b/deploy/snap/README.md new file mode 100644 index 000000000..4950f4921 --- /dev/null +++ b/deploy/snap/README.md @@ -0,0 +1,183 @@ +# Building a snap package + +OpenShell snap packages are defined by the root `snapcraft.yaml` and built with +Snapcraft from source. + +The helper task under `tasks/` still stages the same payload from pre-built +binaries when you want to inspect the snap root or produce local artifacts. + +## Prerequisites + +- Linux on `amd64` or `arm64` +- `snap` from `snapd` +- `snapcraft` +- Docker from the Docker snap (`sudo snap install docker`) + +## Build with Snapcraft + +Build the snap from source with the root manifest: + +```shell +snapcraft pack +``` + +The manifest builds the Rust binaries inside Snapcraft, installs the CLI, +gateway, and sandbox supervisor into the snap, and keeps the same runtime +environment as the current deployment logic. + +## Staged helper flow + +The helper task under `tasks/` still stages the same payload from pre-built +binaries when you want to inspect the snap root or produce local artifacts. + +For that flow, install `mise` and build: + +- `openshell` +- `openshell-gateway` +- `openshell-sandbox` + +## Build helper binaries + +Build the release binaries used by the staged helper flow: + +```shell +mise run build:rust:snap +``` + +This convenience target builds the CLI with `bundled-z3`, the gateway, and +`openshell-sandbox` for the Docker driver to bind-mount into sandbox containers. + +## Pack the snap + +Run the packaging hook through mise: + +```shell +VERSION="$(uv run python tasks/scripts/release.py get-version --snap)" + +OPENSHELL_CLI_BINARY="$PWD/target/release/openshell" \ +OPENSHELL_GATEWAY_BINARY="$PWD/target/release/openshell-gateway" \ +OPENSHELL_DOCKER_SUPERVISOR_BINARY="$PWD/target/release/openshell-sandbox" \ +OPENSHELL_SNAP_VERSION="$VERSION" \ +OPENSHELL_OUTPUT_DIR=artifacts \ + mise run package:snap +``` + +The artifact is written to `artifacts/openshell_${VERSION}_${ARCH}.snap`. The +packaging hook fails before `snap pack` if `openshell-sandbox` is missing or not +executable. + +## Stage without packing + +To inspect the snap root without running `snap pack`: + +```shell +VERSION="$(uv run python tasks/scripts/release.py get-version --snap)" + +OPENSHELL_CLI_BINARY="$PWD/target/release/openshell" \ +OPENSHELL_GATEWAY_BINARY="$PWD/target/release/openshell-gateway" \ +OPENSHELL_DOCKER_SUPERVISOR_BINARY="$PWD/target/release/openshell-sandbox" \ +OPENSHELL_SNAP_VERSION="$VERSION" \ + mise run package:snap:stage +``` + +The staged root is written to `artifacts/snap-root`. + +## Commands in the snap + +The snap exposes the CLI: + +- `openshell` + +It also defines a system service running the gateway with the Docker driver. + +- `openshell.gateway` + +The gateway service uses `refresh-mode: endure` so snap refreshes do not restart +it while sandboxes are active. Restart the service manually when you are ready +to move the gateway to the refreshed snap revision. + +`openshell-sandbox` is staged next to `openshell-gateway` as the Docker +supervisor binary. The gateway app passes it to the in-process Docker driver +through `OPENSHELL_DOCKER_SUPERVISOR_BIN=$SNAP/bin/openshell-sandbox`. The +service stores its gateway database under `$SNAP_COMMON`. + +## Interfaces + +The `openshell` CLI app plugs: + +- `home` +- `network` +- `ssh-keys` +- `system-observe` + +The `openshell.gateway` service plugs: + +- `docker` +- `log-observe` +- `network` +- `network-bind` +- `ssh-keys` +- `system-observe` + +## Start a Docker gateway from the snap + +The snapped gateway talks to Docker through the Docker snap's +`docker:docker-daemon` slot. The snap declares `default-provider: docker` on +its Docker plug so snapd can install the Docker snap when OpenShell is +installed. Connect the interface before using the Docker driver: + +```shell +sudo snap connect openshell:docker docker:docker-daemon +sudo snap connect openshell:log-observe +sudo snap connect openshell:system-observe +sudo snap connect openshell:ssh-keys +``` + +The gateway uses Docker's default Unix socket location. The Docker snap exposes +that socket through the connected `docker` interface, so no `DOCKER_HOST` +override is required. The OpenShell snap still requires the Docker snap because +it relies on the `docker:docker-daemon` slot; it does not work with Docker +installed from a Debian package or Docker's upstream packages. + +The service runs the gateway with the Docker driver enabled: + +```shell +openshell.gateway \ + --drivers docker \ + --disable-tls \ + --port 17670 \ + --db-url "sqlite:$SNAP_COMMON/gateway.db?mode=rwc" \ + --docker-supervisor-bin "$SNAP/bin/openshell-sandbox" \ + --docker-network-name openshell-snap \ + --sandbox-namespace docker-snap \ + --sandbox-image ghcr.io/nvidia/openshell-community/sandboxes/base:latest \ + --sandbox-image-pull-policy IfNotPresent \ + --grpc-endpoint http://host.openshell.internal:17670 +``` + +This stores the gateway SQLite database at +`/var/snap/openshell/common/gateway.db`. + +## Connect with the OpenShell CLI + +Register the snap-run gateway as a local plaintext gateway: + +```shell +openshell gateway add http://127.0.0.1:17670 --local --name snap-docker +openshell gateway select snap-docker +openshell status +``` + +Then use normal sandbox commands: + +```shell +openshell sandbox create --name demo +openshell sandbox connect demo +``` + +To avoid changing the default gateway, pass the gateway name per command: + +```shell +openshell --gateway snap-docker status +openshell --gateway snap-docker sandbox create --name demo +``` diff --git a/deploy/snap/meta/snap.yaml.in b/deploy/snap/meta/snap.yaml.in new file mode 100644 index 000000000..9444fbbf7 --- /dev/null +++ b/deploy/snap/meta/snap.yaml.in @@ -0,0 +1,66 @@ +# SPDX-FileCopyrightText: Copyright (c) 2025-2026 NVIDIA CORPORATION & AFFILIATES. All rights reserved. +# SPDX-License-Identifier: Apache-2.0 + +name: openshell +title: OpenShell +version: "@VERSION@" +summary: Safe, sandboxed runtimes for autonomous AI agents +description: | + OpenShell provides safe, sandboxed runtimes for autonomous AI agents. + It offers a CLI for managing gateways, sandboxes, and providers with + policy-enforced egress routing, credential proxying, and privacy-aware + LLM inference routing. + +base: "@BASE@" +grade: "@GRADE@" +confinement: strict +license: Apache-2.0 +website: https://docs.nvidia.com/openshell/latest/index.html +source-code: https://github.com/NVIDIA/OpenShell +issues: https://github.com/NVIDIA/OpenShell/issues +contact: https://github.com/NVIDIA/OpenShell/security/policy +architectures: + - "@ARCH@" + +apps: + openshell: + command: bin/openshell + plugs: + - home + - network + - ssh-keys + - system-observe + gateway: + command: bin/openshell-gateway + daemon: simple + refresh-mode: endure + environment: + OPENSHELL_BIND_ADDRESS: 127.0.0.1 + OPENSHELL_SERVER_PORT: 17670 + OPENSHELL_DB_URL: "sqlite:$SNAP_COMMON/gateway.db?mode=rwc" + OPENSHELL_GRPC_ENDPOINT: http://host.openshell.internal:17670 + OPENSHELL_DISABLE_TLS: true + OPENSHELL_DRIVERS: docker + OPENSHELL_DOCKER_SUPERVISOR_BIN: "$SNAP/bin/openshell-sandbox" + OPENSHELL_DOCKER_NETWORK_NAME: openshell-snap + OPENSHELL_SANDBOX_IMAGE: ghcr.io/nvidia/openshell-community/sandboxes/base:latest + OPENSHELL_SANDBOX_IMAGE_PULL_POLICY: IfNotPresent + OPENSHELL_SANDBOX_SSH_PORT: 2222 + OPENSHELL_SSH_GATEWAY_HOST: 127.0.0.1 + OPENSHELL_SSH_GATEWAY_PORT: 8080 + XDG_DATA_HOME: "$SNAP_COMMON" + # Used for creating and locating certain sockets. + XDG_RUNTIME_DIR: "$SNAP_COMMON" + + plugs: + - docker + - log-observe + - network + - network-bind + - ssh-keys + - system-observe + +plugs: + docker: + interface: docker + default-provider: docker diff --git a/snapcraft.yaml b/snapcraft.yaml new file mode 100644 index 000000000..9ac0a14c7 --- /dev/null +++ b/snapcraft.yaml @@ -0,0 +1,102 @@ +# SPDX-FileCopyrightText: Copyright (c) 2025-2026 NVIDIA CORPORATION & AFFILIATES. All rights reserved. +# SPDX-License-Identifier: Apache-2.0 + +name: openshell +title: OpenShell +adopt-info: openshell +summary: Safe, sandboxed runtimes for autonomous AI agents +description: | + OpenShell provides safe, sandboxed runtimes for autonomous AI agents. + It offers a CLI for managing gateways, sandboxes, and providers with + policy-enforced egress routing, credential proxying, and privacy-aware + LLM inference routing. + +base: core24 +# TODO: this prevents publishing a snap to latest/stable. +grade: devel +confinement: strict +license: Apache-2.0 +website: https://docs.nvidia.com/openshell/latest/index.html +source-code: https://github.com/NVIDIA/OpenShell +issues: https://github.com/NVIDIA/OpenShell/issues +contact: https://github.com/NVIDIA/OpenShell/security/policy +platforms: + amd64: + build-on: [amd64] + build-for: [amd64] + arm64: + build-on: [arm64] + build-for: [arm64] + +apps: + openshell: + command: bin/openshell + plugs: + - home + - network + - ssh-keys + - system-observe + gateway: + command: bin/openshell-gateway + daemon: simple + refresh-mode: endure + environment: + OPENSHELL_BIND_ADDRESS: 127.0.0.1 + OPENSHELL_SERVER_PORT: 17670 + OPENSHELL_DB_URL: "sqlite:$SNAP_COMMON/gateway.db?mode=rwc" + OPENSHELL_GRPC_ENDPOINT: http://host.openshell.internal:17670 + OPENSHELL_DISABLE_TLS: "true" + OPENSHELL_DRIVERS: docker + OPENSHELL_DOCKER_SUPERVISOR_BIN: "$SNAP/bin/openshell-sandbox" + OPENSHELL_DOCKER_NETWORK_NAME: openshell-snap + OPENSHELL_SANDBOX_IMAGE: ghcr.io/nvidia/openshell-community/sandboxes/base:latest + OPENSHELL_SANDBOX_IMAGE_PULL_POLICY: IfNotPresent + OPENSHELL_SANDBOX_SSH_PORT: 2222 + OPENSHELL_SSH_GATEWAY_HOST: 127.0.0.1 + OPENSHELL_SSH_GATEWAY_PORT: 8080 + XDG_DATA_HOME: "$SNAP_COMMON" + XDG_RUNTIME_DIR: "$SNAP_COMMON" + plugs: + - docker + - log-observe + - network + - network-bind + - ssh-keys + - system-observe + +parts: + openshell: + plugin: rust + rust-channel: "1.95.0" + source: . + build-packages: + - build-essential + - ca-certificates + - clang + - cmake + - git + - libclang-dev + - libssl-dev + - libz3-dev + - pkg-config + - python3 + override-pull: | + craftctl default + craftctl set version="$(python3 "$CRAFT_PROJECT_DIR/tasks/scripts/release.py" get-version --snap)" + override-build: | + set -euo pipefail + + cargo build --release --locked -p openshell-cli --features bundled-z3 + cargo build --release --locked -p openshell-server --bin openshell-gateway + cargo build --release --locked -p openshell-sandbox --bin openshell-sandbox + + install -D -m 0755 "$CRAFT_PROJECT_DIR/target/release/openshell" \ + "$CRAFT_PART_INSTALL/bin/openshell" + install -D -m 0755 "$CRAFT_PROJECT_DIR/target/release/openshell-gateway" \ + "$CRAFT_PART_INSTALL/bin/openshell-gateway" + install -D -m 0755 "$CRAFT_PROJECT_DIR/target/release/openshell-sandbox" \ + "$CRAFT_PART_INSTALL/bin/openshell-sandbox" + install -D -m 0644 "$CRAFT_PROJECT_DIR/LICENSE" \ + "$CRAFT_PART_INSTALL/usr/share/doc/openshell/LICENSE" + install -D -m 0644 "$CRAFT_PROJECT_DIR/README.md" \ + "$CRAFT_PART_INSTALL/usr/share/doc/openshell/README.md" diff --git a/tasks/ci.toml b/tasks/ci.toml index c5728168b..791c4b3b0 100644 --- a/tasks/ci.toml +++ b/tasks/ci.toml @@ -27,6 +27,14 @@ description = "Build all Rust crates in release mode" run = "cargo build --workspace --release" hide = true +["build:rust:snap"] +description = "Build release Rust binaries consumed by the hand-staged snap" +run = [ + "cargo build --release -p openshell-cli --features bundled-z3", + "cargo build --release -p openshell-server", + "cargo build --release -p openshell-sandbox", +] + [check] description = "Run fast compile and type checks" depends = ["rust:check", "python:typecheck"] diff --git a/tasks/package.toml b/tasks/package.toml index 496f93de2..08436a632 100644 --- a/tasks/package.toml +++ b/tasks/package.toml @@ -23,3 +23,26 @@ hide = true ["package:deb:install"] description = "Build OpenShell from source and install the deb locally (requires sudo)" run = "tasks/scripts/package-deb-install.sh" + +["package:snap"] +description = "Build a snap package from supplied OpenShell binaries" +run = "tasks/scripts/package-snap.sh" +hide = true + +["package:snap:amd64"] +description = "Build an amd64 snap package from supplied OpenShell binaries" +env = { OPENSHELL_SNAP_ARCH = "amd64" } +run = "tasks/scripts/package-snap.sh" +hide = true + +["package:snap:arm64"] +description = "Build an arm64 snap package from supplied OpenShell binaries" +env = { OPENSHELL_SNAP_ARCH = "arm64" } +run = "tasks/scripts/package-snap.sh" +hide = true + +["package:snap:stage"] +description = "Stage a snap root from supplied OpenShell binaries without running snap pack" +env = { OPENSHELL_SNAP_PACK = "0", OPENSHELL_SNAP_STAGE_DIR = "artifacts/snap-root" } +run = "tasks/scripts/package-snap.sh" +hide = true diff --git a/tasks/scripts/package-snap.sh b/tasks/scripts/package-snap.sh new file mode 100755 index 000000000..4aafa4ca4 --- /dev/null +++ b/tasks/scripts/package-snap.sh @@ -0,0 +1,215 @@ +#!/usr/bin/env bash +# SPDX-FileCopyrightText: Copyright (c) 2025-2026 NVIDIA CORPORATION & AFFILIATES. All rights reserved. +# SPDX-License-Identifier: Apache-2.0 +# +# Stage a hand-authored OpenShell snap payload from pre-built binaries, then +# optionally invoke `snap pack` against the staged directory. + +set -euo pipefail + +APP_NAME="openshell" + +usage() { + cat <<'EOF' +Build or stage the openshell snap from supplied binaries. + +Required environment: + OPENSHELL_CLI_BINARY Path to openshell + OPENSHELL_GATEWAY_BINARY Path to openshell-gateway + OPENSHELL_DOCKER_SUPERVISOR_BINARY + Path to the Linux openshell-sandbox supervisor + OPENSHELL_SNAP_VERSION Snap version + +Optional environment: + OPENSHELL_SNAP_ARCH Snap architecture (amd64 or arm64; defaults to host arch) + OPENSHELL_SNAP_BASE Snap base (default: core24) + OPENSHELL_SNAP_GRADE Snap grade (default: devel) + OPENSHELL_SNAP_PACK Set to 0 to only stage the snap root (default: 1) + OPENSHELL_SNAP_STAGE_DIR Directory to stage into (default: temporary directory) + OPENSHELL_OUTPUT_DIR Output directory for .snap artifacts (default: artifacts) +EOF +} + +require_env() { + local name="$1" + if [ -z "${!name:-}" ]; then + echo "error: ${name} is required" >&2 + usage >&2 + exit 2 + fi +} + +stage_binary() { + local src="$1" + local dst="$2" + if [ ! -x "$src" ]; then + echo "error: binary is missing or not executable: ${src}" >&2 + exit 1 + fi + mkdir -p "$(dirname "$dst")" + install -m 0755 "$src" "$dst" +} + +infer_snap_arch() { + case "$(uname -m)" in + x86_64 | amd64) echo "amd64" ;; + aarch64 | arm64) echo "arm64" ;; + *) uname -m ;; + esac +} + +normalize_bool() { + case "${1,,}" in + 1 | true | yes | on) echo "1" ;; + 0 | false | no | off) echo "0" ;; + *) + echo "error: invalid boolean value '${1}' (expected true/false, 1/0, yes/no, on/off)" >&2 + exit 2 + ;; + esac +} + +prepare_stage_dir() { + local dir="$1" + + if [ -z "$dir" ]; then + echo "error: refusing empty OPENSHELL_SNAP_STAGE_DIR" >&2 + exit 2 + fi + + mkdir -p "$dir" + + local canonical_dir + local canonical_repo_root + canonical_dir="$(cd "$dir" && pwd -P)" + canonical_repo_root="$(cd "$repo_root" && pwd -P)" + + if [[ "$canonical_dir" == "/" || + "$canonical_dir" == "$canonical_repo_root" || + "$canonical_repo_root" == "$canonical_dir/"* ]]; then + echo "error: refusing unsafe OPENSHELL_SNAP_STAGE_DIR: '${dir}' resolved to '${canonical_dir}'" >&2 + exit 2 + fi + + find "$canonical_dir" -mindepth 1 -maxdepth 1 -exec rm -rf -- {} + +} + +render_snap_yaml() { + local template="$1" + local output="$2" + + awk \ + -v version="$OPENSHELL_SNAP_VERSION" \ + -v base="$OPENSHELL_SNAP_BASE" \ + -v grade="$OPENSHELL_SNAP_GRADE" \ + -v arch="$OPENSHELL_SNAP_ARCH" ' + { + gsub(/@VERSION@/, version); + gsub(/@BASE@/, base); + gsub(/@GRADE@/, grade); + gsub(/@ARCH@/, arch); + print; + } + ' "$template" >"$output" +} + +# --------------------------------------------------------------------------- +# Inputs +# --------------------------------------------------------------------------- + +require_env OPENSHELL_CLI_BINARY +require_env OPENSHELL_GATEWAY_BINARY +require_env OPENSHELL_DOCKER_SUPERVISOR_BINARY +require_env OPENSHELL_SNAP_VERSION + +OPENSHELL_SNAP_ARCH="${OPENSHELL_SNAP_ARCH:-$(infer_snap_arch)}" +OPENSHELL_SNAP_BASE="${OPENSHELL_SNAP_BASE:-core24}" +OPENSHELL_SNAP_GRADE="${OPENSHELL_SNAP_GRADE:-devel}" +OPENSHELL_SNAP_PACK="$(normalize_bool "${OPENSHELL_SNAP_PACK:-1}")" + +case "$OPENSHELL_SNAP_ARCH" in +amd64 | arm64) ;; +*) + echo "error: OPENSHELL_SNAP_ARCH must be amd64 or arm64, got ${OPENSHELL_SNAP_ARCH}" >&2 + exit 2 + ;; +esac + +case "$OPENSHELL_SNAP_GRADE" in +devel | stable) ;; +*) + echo "error: OPENSHELL_SNAP_GRADE must be devel or stable, got ${OPENSHELL_SNAP_GRADE}" >&2 + exit 2 + ;; +esac + +repo_root="$(cd "$(dirname "$0")/../.." && pwd)" +src_dir="${repo_root}/deploy/snap" +template="${src_dir}/meta/snap.yaml.in" +output_dir_input="${OPENSHELL_OUTPUT_DIR:-artifacts}" +case "$output_dir_input" in +/*) output_dir="$output_dir_input" ;; +*) output_dir="${repo_root}/${output_dir_input}" ;; +esac +mkdir -p "$output_dir" + +if [ ! -f "$template" ]; then + echo "error: snap metadata template not found: ${template}" >&2 + exit 1 +fi + +tmpdir="$(mktemp -d)" +cleanup() { + rm -rf "$tmpdir" +} +trap cleanup EXIT + +if [ -n "${OPENSHELL_SNAP_STAGE_DIR:-}" ]; then + case "$OPENSHELL_SNAP_STAGE_DIR" in + /*) snap_root="$OPENSHELL_SNAP_STAGE_DIR" ;; + *) snap_root="${repo_root}/${OPENSHELL_SNAP_STAGE_DIR}" ;; + esac + prepare_stage_dir "$snap_root" +else + snap_root="${tmpdir}/snap-root" + mkdir -p "$snap_root" +fi + +# --------------------------------------------------------------------------- +# Stage the snap payload +# --------------------------------------------------------------------------- + +stage_binary "$OPENSHELL_CLI_BINARY" "$snap_root/bin/openshell" +stage_binary "$OPENSHELL_GATEWAY_BINARY" "$snap_root/bin/openshell-gateway" +stage_binary "$OPENSHELL_DOCKER_SUPERVISOR_BINARY" "$snap_root/bin/openshell-sandbox" + +install -D -m 0644 "${repo_root}/LICENSE" "$snap_root/usr/share/doc/openshell/LICENSE" +install -D -m 0644 "${repo_root}/README.md" "$snap_root/usr/share/doc/openshell/README.md" + +mkdir -p "$snap_root/meta" +render_snap_yaml "$template" "$snap_root/meta/snap.yaml" + +# --------------------------------------------------------------------------- +# Smoke tests +# --------------------------------------------------------------------------- + +"$snap_root/bin/openshell" --version +"$snap_root/bin/openshell-gateway" --version +"$snap_root/bin/openshell-sandbox" --version + +# --------------------------------------------------------------------------- +# Pack +# --------------------------------------------------------------------------- + +if [ "$OPENSHELL_SNAP_PACK" = "0" ]; then + echo "Staged snap root at ${snap_root}" + exit 0 +fi + +if ! command -v snap >/dev/null 2>&1; then + echo "error: snap command not found; install snapd or set OPENSHELL_SNAP_PACK=0 to only stage the snap root" >&2 + exit 1 +fi + +snap pack "$snap_root" "$output_dir" +echo "Wrote ${output_dir}/${APP_NAME}_${OPENSHELL_SNAP_VERSION}_${OPENSHELL_SNAP_ARCH}.snap" diff --git a/tasks/scripts/release.py b/tasks/scripts/release.py index 4e839bbf0..68437f384 100644 --- a/tasks/scripts/release.py +++ b/tasks/scripts/release.py @@ -22,6 +22,7 @@ class Versions: cargo: str docker: str deb: str + snap: str rpm_version: str rpm_release: str git_tag: str @@ -108,6 +109,11 @@ def _versions_from_parts( # Docker tags can't contain '+'. docker_version = cargo_version.replace("+", "-") + # Snap versions cannot contain '+' and are limited to 32 characters. + snap_version = re.sub(r"\.d\d{8}$", "", docker_version) + if len(snap_version) > 32: + raise ValueError(f"snap version must be at most 32 characters: {snap_version}") + # Debian versions use '~' so prereleases sort before the eventual release. deb_version = cargo_version deb_version = deb_version[1:] if deb_version.startswith("v") else deb_version @@ -119,6 +125,7 @@ def _versions_from_parts( cargo=cargo_version, docker=docker_version, deb=deb_version, + snap=snap_version, rpm_version=rpm_version, rpm_release=rpm_release, git_tag=git_tag, @@ -149,6 +156,7 @@ def _print_env(versions: Versions) -> None: print(f"VERSION_CARGO={versions.cargo}") print(f"VERSION_DOCKER={versions.docker}") print(f"VERSION_DEB={versions.deb}") + print(f"VERSION_SNAP={versions.snap}") print(f"VERSION_RPM={versions.rpm_version}") print(f"VERSION_RPM_RELEASE={versions.rpm_release}") print(f"GIT_TAG={versions.git_tag}") @@ -166,6 +174,8 @@ def get_version(format: str) -> None: print(versions.docker) elif format == "deb": print(versions.deb) + elif format == "snap": + print(versions.snap) elif format == "rpm-version": print(versions.rpm_version) elif format == "rpm-release": @@ -368,6 +378,9 @@ def build_parser() -> argparse.ArgumentParser: get_version_parser.add_argument( "--deb", action="store_true", help="Print Debian package version only." ) + get_version_parser.add_argument( + "--snap", action="store_true", help="Print Snap package version only." + ) get_version_parser.add_argument( "--rpm-version", action="store_true", help="Print RPM Version only." ) @@ -416,6 +429,8 @@ def main() -> None: get_version("docker") elif args.deb: get_version("deb") + elif args.snap: + get_version("snap") elif args.rpm_version: get_version("rpm-version") elif args.rpm_release: