Skip to content

Commit

Permalink
feat: added basic task markdown generation
Browse files Browse the repository at this point in the history
  • Loading branch information
jdx committed Sep 27, 2024
1 parent 04776a9 commit bc924f5
Show file tree
Hide file tree
Showing 14 changed files with 201 additions and 10 deletions.
1 change: 1 addition & 0 deletions .markdownlintignore
Original file line number Diff line number Diff line change
Expand Up @@ -3,3 +3,4 @@ target/
CHANGELOG.md
docs/node_modules/
node_modules/
test/
5 changes: 3 additions & 2 deletions Cargo.lock

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

2 changes: 1 addition & 1 deletion Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -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"
Expand Down
3 changes: 3 additions & 0 deletions docs/.vitepress/cli_commands.ts
Original file line number Diff line number Diff line change
Expand Up @@ -111,6 +111,9 @@ export const commands: { [key: string]: Command } = {
"github-action": {
hide: false,
},
"task-docs": {
hide: false,
},
},
},
"global": {
Expand Down
28 changes: 28 additions & 0 deletions docs/cli/generate/task-docs.md
Original file line number Diff line number Diff line change
@@ -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
```
29 changes: 29 additions & 0 deletions docs/cli/index.md
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand Down
8 changes: 6 additions & 2 deletions docs/tasks/running-tasks.md
Original file line number Diff line number Diff line change
Expand Up @@ -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:
Expand Down
17 changes: 15 additions & 2 deletions mise.usage.kdl
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand Down Expand Up @@ -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.
Expand Down
5 changes: 4 additions & 1 deletion src/cli/generate/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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,
Expand All @@ -15,13 +16,15 @@ pub struct Generate {
enum Commands {
GitPreCommit(git_pre_commit::GitPreCommit),
GithubAction(github_action::GithubAction),
TaskDocs(task_docs::TaskDocs),
}

impl Commands {
pub fn run(self) -> eyre::Result<()> {
match self {
Self::GitPreCommit(cmd) => cmd.run(),
Self::GithubAction(cmd) => cmd.run(),
Self::TaskDocs(cmd) => cmd.run(),
}
}
}
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,9 @@
---
source: src/cli/generate/task_docs.rs
expression: output
---
# `filetask`

## Flag `--user <user>`
The user to run as
95 changes: 95 additions & 0 deletions src/cli/generate/task_docs.rs
Original file line number Diff line number Diff line change
@@ -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();
}
}
2 changes: 1 addition & 1 deletion src/cli/global.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand Down
2 changes: 1 addition & 1 deletion src/config/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -252,7 +252,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()
Expand Down
5 changes: 5 additions & 0 deletions src/task/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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> {
Expand Down

0 comments on commit bc924f5

Please sign in to comment.