Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
18 commits
Select commit Hold shift + click to select a range
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -108,6 +108,7 @@ shellexpand = "3.1.0"
spdx = "0.10"
spfs = { path = "crates/spfs" }
spfs-cli-common = { path = "crates/spfs-cli/common" }
spfs-cli-main = { path = "crates/spfs-cli/main" }
spfs-encoding = { path = "crates/spfs-encoding" }
spfs-vfs = { path = "crates/spfs-vfs" }
spk-build = { path = "crates/spk-build" }
Expand Down
30 changes: 30 additions & 0 deletions crates/spfs-cli/cmd-docs/Cargo.toml
Original file line number Diff line number Diff line change
@@ -0,0 +1,30 @@
[package]
authors = { workspace = true }
edition = { workspace = true }
name = "spfs-cli-docs"
version = { workspace = true }
license-file = { workspace = true }
homepage = { workspace = true }
repository = { workspace = true }
readme = { workspace = true }
description = { workspace = true }

[lints]
workspace = true

[[bin]]
name = "spfs-docs"
path = "src/cmd_docs.rs"

[features]
sentry = ["spfs-cli-common/sentry"]

[dependencies]
clap = { workspace = true }
clap-markdown = "0.1.5"
miette = { workspace = true, features = ["fancy"] }
spfs = { workspace = true }
spfs-cli-common = { workspace = true }
spfs-cli-main = { workspace = true }
tokio = { workspace = true, features = ["rt", "rt-multi-thread"] }
tracing = { workspace = true }
35 changes: 35 additions & 0 deletions crates/spfs-cli/cmd-docs/src/cmd_docs.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,35 @@
// Copyright (c) Contributors to the SPK project.
// SPDX-License-Identifier: Apache-2.0
// https://github.com/spkenv/spk

use std::fs;

use clap::Parser;
use miette::Result;
use spfs_cli_common as cli;

cli::main!(CmdDocs);

use spfs_cli_main::cmd_spfs::Opt;

/// Write Markdown documentation for all SPFS subcommands to docs folder.
#[derive(Debug, Parser)]
#[clap(name = "spfs-docs")]
pub struct CmdDocs {
#[clap(flatten)]
pub logging: cli::Logging,
}

impl CmdDocs {
pub async fn run(&mut self, _config: &spfs::Config) -> Result<i32> {
let mut markdown = clap_markdown::help_markdown::<Opt>();
markdown.insert(0, '\n');
markdown.insert_str(0, "---\n");
markdown.insert_str(0, "chapter: true\n");
markdown.insert_str(0, "title: SPFS CLI\n");
markdown.insert_str(0, "---\n");
// This path is relative to the current shell directory.
fs::write("docs/spfs/cli/markdown.md", markdown).expect("Unable to write file");
Ok(0)
}
}
4 changes: 4 additions & 0 deletions crates/spfs-cli/main/Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -17,6 +17,10 @@ workspace = true
name = "spfs"
path = "src/bin.rs"

[lib]
name = "spfs_cli_main"
path = "src/lib.rs"

[features]
fuse = ["spfs/fuse-backend"]
sentry = ["spfs-cli-common/sentry"]
Expand Down
175 changes: 2 additions & 173 deletions crates/spfs-cli/main/src/bin.rs
Original file line number Diff line number Diff line change
Expand Up @@ -2,179 +2,8 @@
// SPDX-License-Identifier: Apache-2.0
// https://github.com/spkenv/spk

use clap::{Parser, Subcommand};
use miette::Result;
use spfs::{Error, OsErrorExt};

mod cmd_check;
mod cmd_commit;
mod cmd_config;
mod cmd_diff;
mod cmd_edit;
mod cmd_info;
mod cmd_init;
mod cmd_layers;
mod cmd_log;
mod cmd_ls;
mod cmd_ls_tags;
mod cmd_migrate;
mod cmd_platforms;
mod cmd_pull;
mod cmd_push;
mod cmd_read;
mod cmd_reset;
mod cmd_run;
mod cmd_runtime;
mod cmd_runtime_info;
mod cmd_runtime_list;
mod cmd_runtime_prune;
mod cmd_runtime_remove;
mod cmd_search;
#[cfg(feature = "server")]
mod cmd_server;
mod cmd_shell;
mod cmd_tag;
mod cmd_tags;
mod cmd_untag;
mod cmd_version;
mod cmd_write;

use clap::Parser;
use spfs_cli_common as cli;
use spfs_cli_common::CommandName;
use spfs_cli_main::cmd_spfs::Opt;

cli::main!(Opt);

/// Filesystem isolation, capture and distribution.
#[derive(Debug, Parser)]
#[clap(
about,
after_help = "EXTERNAL SUBCOMMANDS:\
\n render render the contents of an environment or layer\
\n monitor watch a runtime and clean it up when complete\
"
)]
pub struct Opt {
#[clap(flatten)]
pub logging: cli::Logging,
#[clap(subcommand)]
pub cmd: Command,
}

#[derive(strum::AsRefStr, Debug, Subcommand)]
#[strum(serialize_all = "lowercase")]
#[clap(trailing_var_arg = true, dont_delimit_trailing_values = true)]
pub enum Command {
Version(cmd_version::CmdVersion),
Init(cmd_init::CmdInit),
Edit(cmd_edit::CmdEdit),
Commit(cmd_commit::CmdCommit),
Config(cmd_config::CmdConfig),
Reset(cmd_reset::CmdReset),
Run(cmd_run::CmdRun),
Tag(cmd_tag::CmdTag),
Untag(cmd_untag::CmdUntag),
Shell(cmd_shell::CmdShell),
Runtime(cmd_runtime::CmdRuntime),
Layers(cmd_layers::CmdLayers),
Platforms(cmd_platforms::CmdPlatforms),
Tags(cmd_tags::CmdTags),
Info(cmd_info::CmdInfo),
Pull(cmd_pull::CmdPull),
Push(cmd_push::CmdPush),
Log(cmd_log::CmdLog),
Search(cmd_search::CmdSearch),
Diff(cmd_diff::CmdDiff),
LsTags(cmd_ls_tags::CmdLsTags),
Ls(cmd_ls::CmdLs),
Migrate(cmd_migrate::CmdMigrate),
Check(cmd_check::CmdCheck),
Read(cmd_read::CmdRead),
Write(cmd_write::CmdWrite),

#[cfg(feature = "server")]
Server(cmd_server::CmdServer),

#[clap(external_subcommand)]
External(Vec<String>),
}

impl CommandName for Opt {
fn command_name(&self) -> &str {
self.cmd.as_ref()
}
}

impl Opt {
async fn run(&mut self, config: &spfs::Config) -> Result<i32> {
match &mut self.cmd {
Command::Version(cmd) => cmd.run().await,
Command::Edit(cmd) => cmd.run(config).await,
Command::Init(cmd) => cmd.run(config).await,
Command::Commit(cmd) => cmd.run(config).await,
Command::Config(cmd) => cmd.run(config).await,
Command::Reset(cmd) => cmd.run(config).await,
Command::Tag(cmd) => cmd.run(config).await,
Command::Untag(cmd) => cmd.run(config).await,
Command::Runtime(cmd) => cmd.run(config).await,
Command::Layers(cmd) => cmd.run(config).await,
Command::Platforms(cmd) => cmd.run(config).await,
Command::Tags(cmd) => cmd.run(config).await,
Command::Info(cmd) => cmd.run(config).await,
Command::Log(cmd) => cmd.run(config).await,
Command::Search(cmd) => cmd.run(config).await,
Command::Diff(cmd) => cmd.run(config).await,
Command::LsTags(cmd) => cmd.run(config).await,
Command::Ls(cmd) => cmd.run(config).await,
Command::Migrate(cmd) => cmd.run(config).await,
Command::Check(cmd) => cmd.run(config).await,
Command::Read(cmd) => cmd.run(config).await,
Command::Write(cmd) => cmd.run(config).await,
Command::Run(cmd) => cmd.run(config).await,
Command::Shell(cmd) => cmd.run(config).await,
Command::Pull(cmd) => cmd.run(config).await,
Command::Push(cmd) => cmd.run(config).await,
#[cfg(feature = "server")]
Command::Server(cmd) => cmd.run(config).await,
Command::External(args) => run_external_subcommand(args.clone()).await,
}
}
}

async fn run_external_subcommand(args: Vec<String>) -> Result<i32> {
{
let mut args = args.into_iter();
let command = match args.next() {
None => {
tracing::error!("Invalid subcommand, cannot be empty");
return Ok(1);
}
Some(c) => c,
};

// either in the PATH or next to the current binary
let cmd_path = match spfs::which_spfs(&command) {
Some(cmd) => cmd,
None => {
let mut p = std::env::current_exe()
.map_err(|err| Error::process_spawn_error("current_exe()", err, None))?;
p.set_file_name(&command);
p
}
};

let cmd = spfs::bootstrap::Command {
executable: cmd_path.into(),
args: args.map(Into::into).collect(),
vars: Vec::new(),
};

match cmd.exec() {
Ok(o) => match o {},
Err(err) if err.is_os_not_found() => {
tracing::error!("{command} not found in PATH, was it properly installed?")
}
Err(err) => tracing::error!("subcommand failed: {err:?}"),
}
Ok(1)
}
}
Loading
Loading