Skip to content

Commit

Permalink
Refactor CLI
Browse files Browse the repository at this point in the history
This standardizes the CLI on noun-verb commands. This also begins
to structure the command line helpers to be more generic and
take in things like config and only do the interactive prompt if
params from a file, stdin, url, or pipe in yaml or json format
is not passed.

Right now only project create functionality has been completely
refactored in this way, but the other commands shouldn't be
much longer
  • Loading branch information
mlieberman85 committed Mar 2, 2024
1 parent 82aadd8 commit 390018b
Show file tree
Hide file tree
Showing 6 changed files with 443 additions and 44 deletions.
99 changes: 95 additions & 4 deletions Cargo.lock

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

69 changes: 56 additions & 13 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -16,28 +16,71 @@ Skootrs is currently pre-release so you will need to use cargo to compile and ru

```shell
$ cargo run
Skootrs is a CLI tool for creating and managing secure-by-default projects. The commands are using noun-verb syntax. So the commands are structured like: `skootrs <noun> <verb>`. For example, `skootrs project create`

Usage: skootrs <COMMAND>

Commands:
create
daemon
dump
get-facet
get
help Print this message or the help of the given subcommand(s)
project Project commands
facet Facet commands
output Output commands
daemon Daemon commands
help Print this message or the help of the given subcommand(s)

Options:
-h, --help Print help (see more with '--help')
```

Project:
```shell
Usage: skootrs project <COMMAND>

Commands:
create Create a new project
get Get the metadata for a particular project
list List all the projects known to the local Skootrs
help Print this message or the help of the given subcommand(s)
```

Facet:
```shell
Facet commands

Usage: skootrs facet <COMMAND>

Commands:
get Get the data for a facet of a particular project
list List all the facets that belong to a particular project
help Print this message or the help of the given subcommand(s)
```

Output:
```shell
Output commands

Usage: skootrs output <COMMAND>

Commands:
get Get the data for a release output of a particular project
list List all the release outputs that belong to a particular project
help Print this message or the help of the given subcommand(s)
```

- Create - The main command for creating a Skootrs secure-by-default repo.
- Daemon - The command for running Skootrs as a REST API.
- Dump - The command for dumping the output of Skootrs' state database to stdout. Useful for debugging.
- Get-Facet - The command for dumping the file or API output for a facet for a given project.
- Get - This is a new command that will be the verb for getting items that Skootrs knows abou
- Output - This is a verb for the Get command that will fetch outputs from Skootrs project. Currently this only supports getting SBOMs
Daemon:
```shell
Daemon commands

Usage: skootrs daemon <COMMAND>

Commands:
start Start the REST server
help Print this message or the help of the given subcommand(s)
```

To get pretty printing of the logs which are in [bunyan](https://github.com/trentm/node-bunyan) format I recommend piping the skootrs into the bunyan cli. I recommend using [bunyan-rs](https://github.com/LukeMathWalker/bunyan). For example:

```shell
$ cargo run create | bunyan ~/Projects/skootrs
$ cargo run project create | bunyan ~/Projects/skootrs
Finished dev [unoptimized + debuginfo] target(s) in 0.19s
Running `target/debug/skootrs-bin create`
> The name of the repository skoot-test-bunyan
Expand Down
5 changes: 5 additions & 0 deletions skootrs-bin/Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -25,3 +25,8 @@ opentelemetry_sdk = "0.21.2"
serde_yaml = "0.9.32"
reqwest = "0.11.24"
base64 = "0.21.7"
clio = { version = "0.3.5", features = ["clap", "clap-parse"] }
serde = "1.0.197"

[build-dependencies]
clap_mangen = "0.2.20"
87 changes: 85 additions & 2 deletions skootrs-bin/src/helpers.rs
Original file line number Diff line number Diff line change
Expand Up @@ -12,14 +12,97 @@ use skootrs_model::{
security_insights::insights10::SecurityInsightsVersion100YamlSchema,
skootrs::{
EcosystemParams, GithubRepoParams, GithubUser, GoParams, InitializedProject, MavenParams,
ProjectParams, RepoParams, SkootError, SourceParams, SUPPORTED_ECOSYSTEMS,
ProjectParams, RepoParams, SkootError, SkootrsConfig, SourceParams, SUPPORTED_ECOSYSTEMS,
},
};
use std::collections::HashMap;

use skootrs_model::skootrs::facet::InitializedFacet;
use skootrs_statestore::SurrealProjectStateStore;

pub struct Project;

impl Project {
/// Returns `Ok(())` if the project creation is successful, otherwise returns an error.
///
/// Creates a new skootrs project by prompting the user for repository details and language selection.
/// The project can be created for either Go or Maven ecosystems right now.
/// The project is created in Github, cloned down, and then initialized along with any other security supporting
/// tasks. If the project_params is not provided, the user will be prompted for the project details.
///
/// # Errors
///
/// Returns an error if the user is not authenticated with Github, or if the project can't be created
/// for any other reason.
pub async fn create<T: ProjectService>(
config: &SkootrsConfig,
project_service: T,
project_params: Option<ProjectParams>,
) -> Result<(), SkootError> {
let project_params = match project_params {
Some(p) => p,
None => Project::prompt_project(config).await?,
};

project_service.initialize(project_params).await?;
Ok(())
}

async fn prompt_project(config: &SkootrsConfig) -> Result<ProjectParams, SkootError> {
let name = Text::new("The name of the repository").prompt()?;
let description = Text::new("The description of the repository").prompt()?;
let user = octocrab::instance().current().user().await?.login;
let Page { items, .. } = octocrab::instance()
.current()
.list_org_memberships_for_authenticated_user()
.send()
.await?;
let organization = inquire::Select::new(
"Select an organization",
items
.iter()
.map(|i| i.organization.login.as_str())
.chain(vec![user.as_str()])
.collect(),
)
.prompt()?;
let language = inquire::Select::new("Select a language", SUPPORTED_ECOSYSTEMS.to_vec());

let gh_org = match organization {
x if x == user => GithubUser::User(x.to_string()),
x => GithubUser::Organization(x.to_string()),
};

let repo_params = match language.prompt()? {
"Go" => RepoParams::Github(GithubRepoParams {
name: name.clone(),
description,
organization: gh_org,
}),
"Maven" => RepoParams::Github(GithubRepoParams {
name: name.clone(),
description,
organization: gh_org,
}),
_ => {
unreachable!("Unsupported language")
}
};

Ok(ProjectParams {
name: name.clone(),
repo_params,
ecosystem_params: EcosystemParams::Go(GoParams {
name: name.clone(),
host: format!("github.com/{organization}"),
}),
source_params: SourceParams {
parent_path: config.local_project_path.clone(),
},
})
}
}

/// Returns `Ok(())` if the project creation is successful, otherwise returns an error.
///
/// Creates a new skootrs project by prompting the user for repository details and language selection.
Expand Down Expand Up @@ -288,4 +371,4 @@ async fn prompt_project() -> Result<InitializedProject, SkootError> {
.ok_or_else(|| SkootError::from("Failed to get selected project"))?;

Ok(project.clone())
}
}
Loading

0 comments on commit 390018b

Please sign in to comment.