feat: added basic task markdown generation (#2677)

This commit is contained in:
jdx 2024-09-27 18:54:15 -05:00 committed by GitHub
parent 398c3ff75e
commit 9e64dd02a6
14 changed files with 201 additions and 10 deletions

View File

@ -3,3 +3,4 @@ target/
CHANGELOG.md
docs/node_modules/
node_modules/
test/

5
Cargo.lock generated
View File

@ -4106,9 +4106,9 @@ checksum = "daf8dba3b7eb870caf1ddeed7bc9d2a049f3cfdfae7cb521b087cc33ae4c49da"
[[package]]
name = "usage-lib"
version = "0.7.4"
version = "0.8.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "ef8f6a44b06866b37810400ecc3ee80dd0354448c870a60d424d6ab364c7e50b"
checksum = "4d2f461138354b775e96629ce474dad0f7b9996dd902db85665de06b8a2aaeda"
dependencies = [
"clap",
"heck 0.5.0",
@ -4120,6 +4120,7 @@ dependencies = [
"once_cell",
"serde",
"strum",
"tera",
"thiserror",
"xx",
]

View File

@ -116,7 +116,7 @@ toml = { version = "0.8", features = ["parse"] }
toml_edit = { version = "0.22", features = ["parse"] }
url = "2.5.0"
#usage-lib = { path = "../usage/lib" }
usage-lib = { version = "0.7.0", features = ["clap"] }
usage-lib = { version = "0.8.0", features = ["clap", "docs"] }
versions = { version = "6.2.0", features = ["serde"] }
vfox = "0.1"
walkdir = "2.5.0"

View File

@ -111,6 +111,9 @@ export const commands: { [key: string]: Command } = {
"github-action": {
hide: false,
},
"task-docs": {
hide: false,
},
},
},
"global": {

View File

@ -0,0 +1,28 @@
## `mise generate task-docs [OPTIONS]` <Badge type="warning" text="experimental" />
```text
[experimental] Generate documentation for tasks in a project
Usage: generate task-docs [OPTIONS]
Options:
-m, --multi
render each task as a separate document, requires `--output` to be a directory
-i, --inject
inserts the documentation into an existing file
This will look for a special comment, <!-- mise-tasks -->, and replace it with the generated documentation.
It will replace everything between the comment and the next comment, <!-- /mise-tasks --> so it can be
run multiple times on the same file to update the documentation.
-I, --index
write only an index of tasks, intended for use with `--multi`
-o, --output <OUTPUT>
writes the generated docs to a file/directory
Examples:
$ mise generate task-docs
```

View File

@ -550,6 +550,35 @@ Examples:
$ git push # runs `mise run ci` on GitHub
```
## `mise generate task-docs [OPTIONS]` <Badge type="warning" text="experimental" />
```text
[experimental] Generate documentation for tasks in a project
Usage: generate task-docs [OPTIONS]
Options:
-m, --multi
render each task as a separate document, requires `--output` to be a directory
-i, --inject
inserts the documentation into an existing file
This will look for a special comment, <!-- mise-tasks -->, and replace it with the generated documentation.
It will replace everything between the comment and the next comment, <!-- /mise-tasks --> so it can be
run multiple times on the same file to update the documentation.
-I, --index
write only an index of tasks, intended for use with `--multi`
-o, --output <OUTPUT>
writes the generated docs to a file/directory
Examples:
$ mise generate task-docs
```
## `mise implode [OPTIONS]`
```text

View File

@ -28,8 +28,12 @@ If there are multiple commands, the args are only passed to the last command.
:::tip
You can define arguments/flags for tasks which will provide validation, parsing, autocomplete, and documentation.
* [Arguments in File Tasks](/tasks/file-tasks.html#arguments)
* [Arguments in TOML Tasks](/tasks/toml-tasks.html#arguments)
* [Arguments in File Tasks](/tasks/file-tasks#arguments)
* [Arguments in TOML Tasks](/tasks/toml-tasks#arguments)
Autocomplete will work automatically for tasks if the `usage` CLI is installed and mise completions are working.
Markdown documentation can be generated with [`mise generate task-docs`](/cli/generate/task-docs).
:::
Multiple tasks/arguments can be separated with this `:::` delimiter:

View File

@ -348,7 +348,7 @@ The "--" separates runtimes from the commands to pass along to the subprocess."#
arg "[COMMAND]..." help="Command string to execute (same as --command)" var=true
}
cmd "generate" subcommand_required=true help="[experimental] Generate files for various tools/services" {
alias "gen"
alias "g"
cmd "git-pre-commit" help="[experimental] Generate a git pre-commit hook" {
alias "pre-commit"
long_help r"[experimental] Generate a git pre-commit hook
@ -387,9 +387,22 @@ when you push changes to your repository."
}
flag "-w --write" help="write to .github/workflows/$name.yml"
}
cmd "task-docs" help="[experimental] Generate documentation for tasks in a project" {
after_long_help r"Examples:
$ mise generate task-docs
"
flag "-m --multi" help="render each task as a separate document, requires `--output` to be a directory"
flag "-i --inject" help="inserts the documentation into an existing file" {
long_help "inserts the documentation into an existing file\n\nThis will look for a special comment, <!-- mise-tasks -->, and replace it with the generated documentation.\nIt will replace everything between the comment and the next comment, <!-- /mise-tasks --> so it can be\nrun multiple times on the same file to update the documentation."
}
flag "-I --index" help="write only an index of tasks, intended for use with `--multi`"
flag "-o --output" help="writes the generated docs to a file/directory" {
arg "<OUTPUT>"
}
}
}
cmd "global" hide=true help="Sets/gets the global tool version(s)" {
alias "g" hide=true
long_help r"Sets/gets the global tool version(s)
Displays the contents of global config after writing.

View File

@ -2,10 +2,11 @@ use clap::Subcommand;
mod git_pre_commit;
mod github_action;
mod task_docs;
/// [experimental] Generate files for various tools/services
#[derive(Debug, clap::Args)]
#[clap(visible_alias = "gen")]
#[clap(visible_alias = "g")]
pub struct Generate {
#[clap(subcommand)]
command: Commands,
@ -15,6 +16,7 @@ pub struct Generate {
enum Commands {
GitPreCommit(git_pre_commit::GitPreCommit),
GithubAction(github_action::GithubAction),
TaskDocs(task_docs::TaskDocs),
}
impl Commands {
@ -22,6 +24,7 @@ impl Commands {
match self {
Self::GitPreCommit(cmd) => cmd.run(),
Self::GithubAction(cmd) => cmd.run(),
Self::TaskDocs(cmd) => cmd.run(),
}
}
}

View File

@ -0,0 +1,9 @@
---
source: src/cli/generate/task_docs.rs
expression: output
---
# `filetask`
## Flag `--user <user>`
The user to run as

View File

@ -0,0 +1,95 @@
use crate::config::settings::SETTINGS;
use crate::config::CONFIG;
use crate::{dirs, file};
use std::path::PathBuf;
/// [experimental] Generate documentation for tasks in a project
#[derive(Debug, clap::Args)]
#[clap(verbatim_doc_comment, after_long_help = AFTER_LONG_HELP)]
pub struct TaskDocs {
/// render each task as a separate document, requires `--output` to be a directory
#[clap(long, short, verbatim_doc_comment)]
multi: bool,
/// inserts the documentation into an existing file
///
/// This will look for a special comment, <!-- mise-tasks -->, and replace it with the generated documentation.
/// It will replace everything between the comment and the next comment, <!-- /mise-tasks --> so it can be
/// run multiple times on the same file to update the documentation.
#[clap(long, short, verbatim_doc_comment)]
inject: bool,
/// write only an index of tasks, intended for use with `--multi`
#[clap(long, short = 'I', verbatim_doc_comment)]
index: bool,
/// writes the generated docs to a file/directory
#[clap(long, short, verbatim_doc_comment)]
output: Option<PathBuf>,
}
impl TaskDocs {
pub fn run(self) -> eyre::Result<()> {
SETTINGS.ensure_experimental("generate task-docs")?;
let tasks = CONFIG.load_tasks_in_dir(dirs::CWD.as_ref().unwrap())?;
let mut out = vec![];
for task in &tasks {
out.push(task.render_markdown()?);
}
if let Some(output) = &self.output {
if self.multi {
if output.is_dir() {
for (i, task) in tasks.iter().enumerate() {
let path = output.join(format!("task-{}.md", i));
file::write(&path, &task.render_markdown()?)?;
}
} else {
return Err(eyre::eyre!(
"`--output` must be a directory when `--multi` is set"
));
}
} else {
let mut doc = String::new();
for task in out {
doc.push_str(&task);
doc.push_str("\n\n");
}
if self.inject {
let mut contents = file::read_to_string(output)?;
let start = contents.find("<!-- mise-tasks -->").unwrap_or(0);
let end = contents[start..]
.find("<!-- /mise-tasks -->")
.unwrap_or(contents.len());
contents.replace_range(start..end, &doc);
file::write(output, &contents)?;
} else {
file::write(output, &doc)?;
}
}
} else {
for task in out {
miseprintln!("{}", task);
}
}
Ok(())
}
}
static AFTER_LONG_HELP: &str = color_print::cstr!(
r#"<bold><underline>Examples:</underline></bold>
$ <bold>mise generate task-docs</bold>
"#
);
#[cfg(test)]
mod tests {
use test_log::test;
use crate::test::{cleanup, reset, setup_git_repo};
#[test]
fn test_task_docs() {
reset();
setup_git_repo();
assert_cli_snapshot!("generate", "task-docs");
cleanup();
}
}

View File

@ -15,7 +15,7 @@ use crate::config::Settings;
///
/// Use `mise local` to set a tool version locally in the current directory.
#[derive(Debug, clap::Args)]
#[clap(verbatim_doc_comment, hide = true, alias = "g", after_long_help = AFTER_LONG_HELP)]
#[clap(verbatim_doc_comment, hide = true, after_long_help = AFTER_LONG_HELP)]
pub struct Global {
/// Tool(s) to add to .tool-versions
/// e.g.: node@20

View File

@ -255,7 +255,7 @@ impl Config {
.collect::<Vec<_>>()
}
fn load_tasks_in_dir(&self, dir: &Path) -> Result<Vec<Task>> {
pub fn load_tasks_in_dir(&self, dir: &Path) -> Result<Vec<Task>> {
let configs = self.configs_at_root(dir);
let config_tasks = configs
.par_iter()

View File

@ -220,6 +220,11 @@ impl Task {
.collect())
}
}
pub fn render_markdown(&self) -> Result<String> {
let (spec, _) = self.parse_usage_spec(None)?;
Ok(spec.render_markdown()?)
}
}
fn name_from_path(root: impl AsRef<Path>, path: impl AsRef<Path>) -> Result<String> {