Move all output from bin/gen
to target/gen
To make it clearer what is and isn't generated content, make gen place all generated output in `target/gen`. Also, try to make the readme clearer about the location of build artifacts. type: development
This commit is contained in:
parent
bf29d74b3e
commit
e7872f56f2
8
.github/workflows/build.yaml
vendored
8
.github/workflows/build.yaml
vendored
|
@ -108,7 +108,7 @@ jobs:
|
||||||
if: matrix.os == 'macos-latest'
|
if: matrix.os == 'macos-latest'
|
||||||
run: |
|
run: |
|
||||||
brew install help2man
|
brew install help2man
|
||||||
cargo run --package gen all
|
cargo run --package gen -- --bin target/debug/imdl all
|
||||||
git diff --no-ext-diff --exit-code
|
git diff --no-ext-diff --exit-code
|
||||||
|
|
||||||
- name: Install `mdbook`
|
- name: Install `mdbook`
|
||||||
|
@ -118,7 +118,7 @@ jobs:
|
||||||
|
|
||||||
- name: Build Book
|
- name: Build Book
|
||||||
run: |
|
run: |
|
||||||
cargo run --package gen book
|
cargo run --package gen -- --bin target/debug/imdl book
|
||||||
mdbook build book --dest-dir ../www/book
|
mdbook build book --dest-dir ../www/book
|
||||||
|
|
||||||
- name: Record Git Revision
|
- name: Record Git Revision
|
||||||
|
@ -139,12 +139,12 @@ jobs:
|
||||||
shell: bash
|
shell: bash
|
||||||
run: ./bin/package ${{github.ref}} ${{matrix.os}} ${{matrix.target}}
|
run: ./bin/package ${{github.ref}} ${{matrix.os}} ${{matrix.target}}
|
||||||
|
|
||||||
- name: Publish
|
- name: Publish Release Archive
|
||||||
uses: softprops/action-gh-release@v1
|
uses: softprops/action-gh-release@v1
|
||||||
if: startsWith(github.ref, 'refs/tags/')
|
if: startsWith(github.ref, 'refs/tags/')
|
||||||
with:
|
with:
|
||||||
draft: false
|
draft: false
|
||||||
files: ${{steps.package.outputs.archive}}
|
files: ${{steps.package.outputs.archive}}
|
||||||
prerelease: false
|
prerelease: true
|
||||||
env:
|
env:
|
||||||
GITHUB_TOKEN: ${{secrets.GITHUB_TOKEN}}
|
GITHUB_TOKEN: ${{secrets.GITHUB_TOKEN}}
|
||||||
|
|
12
.gitignore
vendored
12
.gitignore
vendored
|
@ -1,17 +1,5 @@
|
||||||
**/*.rs.bk
|
**/*.rs.bk
|
||||||
/CHANGELOG.md
|
|
||||||
/book/book
|
/book/book
|
||||||
/book/src/SUMMARY.md
|
|
||||||
/book/src/bittorrent.md
|
|
||||||
/book/src/changelog.md
|
|
||||||
/book/src/commands.md
|
|
||||||
/book/src/commands/
|
|
||||||
/book/src/faq.md
|
|
||||||
/book/src/introduction.md
|
|
||||||
/book/src/references
|
|
||||||
/book/src/references.md
|
|
||||||
/completions
|
|
||||||
/man
|
|
||||||
/target
|
/target
|
||||||
/wiki
|
/wiki
|
||||||
/www/book
|
/www/book
|
||||||
|
|
10
Cargo.lock
generated
10
Cargo.lock
generated
|
@ -354,6 +354,12 @@ version = "1.0.6"
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "2fad85553e09a6f881f739c29f0b00b0f01357c743266d478b68951ce23285f3"
|
checksum = "2fad85553e09a6f881f739c29f0b00b0f01357c743266d478b68951ce23285f3"
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "fs_extra"
|
||||||
|
version = "1.1.0"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "5f2a4a2034423744d2cc7ca2068453168dcdb82c438419e639a26bd87839c674"
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "gen"
|
name = "gen"
|
||||||
version = "0.0.0"
|
version = "0.0.0"
|
||||||
|
@ -363,10 +369,9 @@ dependencies = [
|
||||||
"cargo_toml",
|
"cargo_toml",
|
||||||
"chrono",
|
"chrono",
|
||||||
"fehler",
|
"fehler",
|
||||||
|
"fs_extra",
|
||||||
"git2",
|
"git2",
|
||||||
"globset",
|
"globset",
|
||||||
"ignore",
|
|
||||||
"lexiclean",
|
|
||||||
"libc",
|
"libc",
|
||||||
"log",
|
"log",
|
||||||
"pretty_env_logger",
|
"pretty_env_logger",
|
||||||
|
@ -379,7 +384,6 @@ dependencies = [
|
||||||
"strum_macros",
|
"strum_macros",
|
||||||
"tempfile",
|
"tempfile",
|
||||||
"url",
|
"url",
|
||||||
"walkdir",
|
|
||||||
]
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
|
|
57
README.md
57
README.md
|
@ -31,10 +31,7 @@ For more about the project and its goals, check out
|
||||||
- [Examples](#examples)
|
- [Examples](#examples)
|
||||||
- [FAQ](#faq)
|
- [FAQ](#faq)
|
||||||
- [Notes for Packagers](#notes-for-packagers)
|
- [Notes for Packagers](#notes-for-packagers)
|
||||||
- [Package Artifacts](#package-artifacts)
|
- [Build Artifacts](#build-artifacts)
|
||||||
- [Binary](#binary)
|
|
||||||
- [Man Pages](#man-pages)
|
|
||||||
- [Completion Scripts](#completion-scripts)
|
|
||||||
- [Release Updates](#release-updates)
|
- [Release Updates](#release-updates)
|
||||||
- [Chat](#chat)
|
- [Chat](#chat)
|
||||||
- [Contributing](#contributing)
|
- [Contributing](#contributing)
|
||||||
|
@ -181,46 +178,38 @@ Intermodal is distributed under the
|
||||||
a public domain dedication with a fallback all-permissive license. The SPDX
|
a public domain dedication with a fallback all-permissive license. The SPDX
|
||||||
identifier of the CC0 is [CC0-1.0](https://spdx.org/licenses/CC0-1.0.html).
|
identifier of the CC0 is [CC0-1.0](https://spdx.org/licenses/CC0-1.0.html).
|
||||||
|
|
||||||
### Package Artifacts
|
### Build Artifacts
|
||||||
|
|
||||||
There are three primary build artifacts: the binary, the man pages, and the
|
There are a number of build artifacts: the binary, the man pages, the
|
||||||
shell completion scripts.
|
changelog, and the shell completion scripts.
|
||||||
|
|
||||||
#### Binary
|
The binary is built with `cargo`, and the other artifacts are built `gen`,
|
||||||
|
located in `bin/gen`.
|
||||||
|
|
||||||
The binary is called `imdl`, and can be built with:
|
The binary can be built with:
|
||||||
|
|
||||||
```
|
cargo build --release
|
||||||
cargo build --release
|
|
||||||
```
|
|
||||||
|
|
||||||
After building, the binary will be present at `target/release/imdl`.
|
_`gen` requires [`help2man`](https://www.gnu.org/software/help2man/) to be
|
||||||
|
installed, which is used to generate man pages from subcommand `--help`
|
||||||
|
strings._
|
||||||
|
|
||||||
#### Man Pages
|
The rest of the build artifacts can be built with `gen`:
|
||||||
|
|
||||||
Intermodal has a number of subcommands, each of which has a man page. The man
|
cargo run --package gen -- --bin target/release/imdl all
|
||||||
pages are generated from the `--help` text using
|
|
||||||
[`help2man`](https://www.gnu.org/software/help2man/).
|
|
||||||
|
|
||||||
To generate the man pages, ensure `help2man` is available, and run:
|
_The path to the built `imdl` executable should be passed to `gen` with the `--bin` flag._
|
||||||
|
|
||||||
```
|
After running the above commands, the following table shows the location of the
|
||||||
mkdir -p man
|
built artifacts.
|
||||||
cargo run --package gen man
|
|
||||||
```
|
|
||||||
|
|
||||||
After building, the man pages will be available in `man`.
|
| Artifact | Location |
|
||||||
|
|--------------------|----------------------------|
|
||||||
#### Completion Scripts
|
| Binary | `target/release/imdl` |
|
||||||
|
| Man Pages | `target/gen/man/*` |
|
||||||
Completion scripts are available for a number of shells. To generate them, run:
|
| Completion Scripts | `target/gen/completions/*` |
|
||||||
|
| Changelog | `target/gen/CHANGELOG.md` |
|
||||||
```
|
| Readme | `target/gen/README.md` |
|
||||||
mkdir -p completions
|
|
||||||
cargo run --release completions --dir completions
|
|
||||||
```
|
|
||||||
|
|
||||||
After running, the completion scripts will be available in `completions`.
|
|
||||||
|
|
||||||
### Release Updates
|
### Release Updates
|
||||||
|
|
||||||
|
|
|
@ -11,10 +11,9 @@ askama = "0.9.0"
|
||||||
cargo_toml = "0.8.0"
|
cargo_toml = "0.8.0"
|
||||||
chrono = "0.4.11"
|
chrono = "0.4.11"
|
||||||
fehler = "1.0.0"
|
fehler = "1.0.0"
|
||||||
|
fs_extra = "1.1.0"
|
||||||
git2 = "0.13.1"
|
git2 = "0.13.1"
|
||||||
globset = "0.4.5"
|
globset = "0.4.5"
|
||||||
ignore = "0.4.14"
|
|
||||||
lexiclean = "0.0.1"
|
|
||||||
libc = "0.2.69"
|
libc = "0.2.69"
|
||||||
log = "0.4.8"
|
log = "0.4.8"
|
||||||
pretty_env_logger = "0.4.0"
|
pretty_env_logger = "0.4.0"
|
||||||
|
@ -25,7 +24,6 @@ structopt = "0.3.12"
|
||||||
strum = "0.18.0"
|
strum = "0.18.0"
|
||||||
strum_macros = "0.18.0"
|
strum_macros = "0.18.0"
|
||||||
tempfile = "3.1.0"
|
tempfile = "3.1.0"
|
||||||
walkdir = "2.3.1"
|
|
||||||
|
|
||||||
[dependencies.serde]
|
[dependencies.serde]
|
||||||
version = "1.0.106"
|
version = "1.0.106"
|
||||||
|
|
16
bin/gen/src/arguments.rs
Normal file
16
bin/gen/src/arguments.rs
Normal file
|
@ -0,0 +1,16 @@
|
||||||
|
use crate::common::*;
|
||||||
|
|
||||||
|
#[derive(StructOpt)]
|
||||||
|
pub(crate) struct Arguments {
|
||||||
|
#[structopt(flatten)]
|
||||||
|
options: Options,
|
||||||
|
#[structopt(subcommand)]
|
||||||
|
subcommand: Subcommand,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl Arguments {
|
||||||
|
#[throws]
|
||||||
|
pub(crate) fn run(self) {
|
||||||
|
self.subcommand.run(self.options)?;
|
||||||
|
}
|
||||||
|
}
|
|
@ -2,7 +2,7 @@ use crate::common::*;
|
||||||
|
|
||||||
pub(crate) struct Bin {
|
pub(crate) struct Bin {
|
||||||
path: PathBuf,
|
path: PathBuf,
|
||||||
pub(crate) subcommands: Vec<Subcommand>,
|
pub(crate) subcommands: Vec<BinSubcommand>,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl Bin {
|
impl Bin {
|
||||||
|
@ -22,7 +22,7 @@ impl Bin {
|
||||||
|
|
||||||
#[throws]
|
#[throws]
|
||||||
fn add_subcommands(&mut self, command: &mut Vec<String>) {
|
fn add_subcommands(&mut self, command: &mut Vec<String>) {
|
||||||
let subcommand = Subcommand::new(&self.path, command.clone())?;
|
let subcommand = BinSubcommand::new(&self.path, command.clone())?;
|
||||||
|
|
||||||
for name in &subcommand.subcommands {
|
for name in &subcommand.subcommands {
|
||||||
command.push(name.into());
|
command.push(name.into());
|
||||||
|
|
150
bin/gen/src/bin_subcommand.rs
Normal file
150
bin/gen/src/bin_subcommand.rs
Normal file
|
@ -0,0 +1,150 @@
|
||||||
|
use crate::common::*;
|
||||||
|
|
||||||
|
#[derive(Debug, Ord, PartialOrd, Eq, PartialEq)]
|
||||||
|
pub(crate) struct BinSubcommand {
|
||||||
|
pub(crate) bin: PathBuf,
|
||||||
|
pub(crate) command: Vec<String>,
|
||||||
|
pub(crate) subcommands: Vec<String>,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl BinSubcommand {
|
||||||
|
#[throws]
|
||||||
|
pub(crate) fn new(bin: &Path, command: Vec<String>) -> Self {
|
||||||
|
let wide_help = Command::new(bin)
|
||||||
|
.args(command.as_slice())
|
||||||
|
.env("IMDL_TERM_WIDTH", "200")
|
||||||
|
.arg("--help")
|
||||||
|
.out()?;
|
||||||
|
|
||||||
|
const MARKER: &str = "\nSUBCOMMANDS:\n";
|
||||||
|
|
||||||
|
let mut subcommands = Vec::new();
|
||||||
|
|
||||||
|
if let Some(marker) = wide_help.find(MARKER) {
|
||||||
|
let block = &wide_help[marker + MARKER.len()..];
|
||||||
|
|
||||||
|
for line in block.lines() {
|
||||||
|
let name = line.trim().split_whitespace().next().unwrap();
|
||||||
|
subcommands.push(name.into());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
Self {
|
||||||
|
bin: bin.into(),
|
||||||
|
command,
|
||||||
|
subcommands,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#[throws]
|
||||||
|
fn help(&self) -> String {
|
||||||
|
info!("Getting help for `{}`", self.command_line());
|
||||||
|
|
||||||
|
Command::new(&self.bin)
|
||||||
|
.args(self.command.as_slice())
|
||||||
|
.env("IMDL_TERM_WIDTH", "80")
|
||||||
|
.arg("--help")
|
||||||
|
.out()?
|
||||||
|
}
|
||||||
|
|
||||||
|
#[throws]
|
||||||
|
pub(crate) fn man(&self) -> String {
|
||||||
|
let command_line = self.command_line();
|
||||||
|
|
||||||
|
info!("Generating man page for `{}`", command_line);
|
||||||
|
|
||||||
|
let name = command_line.replace(" ", "\\ ");
|
||||||
|
|
||||||
|
let help = self.help()?;
|
||||||
|
|
||||||
|
let description = if self.command.is_empty() {
|
||||||
|
"A 40' shipping container for the Internet".to_string()
|
||||||
|
} else {
|
||||||
|
help.lines().nth(1).unwrap().into()
|
||||||
|
};
|
||||||
|
|
||||||
|
let include = format!(
|
||||||
|
"\
|
||||||
|
[NAME]
|
||||||
|
\\fB{}\\fR
|
||||||
|
- {}
|
||||||
|
",
|
||||||
|
name, description
|
||||||
|
);
|
||||||
|
|
||||||
|
let tmp = tempfile::tempdir().context(error::Tempdir)?;
|
||||||
|
|
||||||
|
let include_path = tmp.path().join("include");
|
||||||
|
|
||||||
|
fs::write(&include_path, include).context(error::Filesystem {
|
||||||
|
path: &include_path,
|
||||||
|
})?;
|
||||||
|
|
||||||
|
let version = cmd!(&self.bin, "--version")
|
||||||
|
.out()?
|
||||||
|
.split_whitespace()
|
||||||
|
.nth(1)
|
||||||
|
.unwrap()
|
||||||
|
.to_owned();
|
||||||
|
|
||||||
|
info!("Running help2man for `{}`", command_line);
|
||||||
|
|
||||||
|
let mut command = self.bin.as_os_str().to_owned();
|
||||||
|
for arg in &self.command {
|
||||||
|
command.push(" ");
|
||||||
|
command.push(arg);
|
||||||
|
}
|
||||||
|
|
||||||
|
let output = cmd!(
|
||||||
|
"help2man",
|
||||||
|
"--include",
|
||||||
|
&include_path,
|
||||||
|
"--manual",
|
||||||
|
"Intermodal Manual",
|
||||||
|
"--no-info",
|
||||||
|
"--source",
|
||||||
|
&format!("Intermodal {}", version),
|
||||||
|
command
|
||||||
|
)
|
||||||
|
.out()?;
|
||||||
|
|
||||||
|
let man = output
|
||||||
|
.replace("📦 ", "\n")
|
||||||
|
.replace("\n.SS ", "\n.SH ")
|
||||||
|
.replace("\"USAGE:\"", "\"SYNOPSIS:\"");
|
||||||
|
|
||||||
|
let re = Regex::new(r"(?ms).SH DESCRIPTION.*?.SH").unwrap();
|
||||||
|
|
||||||
|
let man = re.replace(&man, ".SH").into_owned();
|
||||||
|
|
||||||
|
man
|
||||||
|
}
|
||||||
|
|
||||||
|
pub(crate) fn slug(&self) -> String {
|
||||||
|
let mut slug = "imdl".to_string();
|
||||||
|
|
||||||
|
for name in &self.command {
|
||||||
|
slug.push('-');
|
||||||
|
slug.push_str(&name);
|
||||||
|
}
|
||||||
|
|
||||||
|
slug
|
||||||
|
}
|
||||||
|
|
||||||
|
pub(crate) fn command_line(&self) -> String {
|
||||||
|
let mut line = "imdl".to_string();
|
||||||
|
|
||||||
|
for name in &self.command {
|
||||||
|
line.push(' ');
|
||||||
|
line.push_str(&name);
|
||||||
|
}
|
||||||
|
|
||||||
|
line
|
||||||
|
}
|
||||||
|
|
||||||
|
#[throws]
|
||||||
|
pub(crate) fn page(&self) -> String {
|
||||||
|
let help = self.help()?;
|
||||||
|
format!("# `{}`\n```\n{}\n```", self.command_line(), help)
|
||||||
|
}
|
||||||
|
}
|
|
@ -17,8 +17,6 @@ pub(crate) use cargo_toml::Manifest;
|
||||||
pub(crate) use chrono::{DateTime, NaiveDateTime, Utc};
|
pub(crate) use chrono::{DateTime, NaiveDateTime, Utc};
|
||||||
pub(crate) use fehler::{throw, throws};
|
pub(crate) use fehler::{throw, throws};
|
||||||
pub(crate) use git2::{Commit, Oid, Repository};
|
pub(crate) use git2::{Commit, Oid, Repository};
|
||||||
pub(crate) use ignore::overrides::OverrideBuilder;
|
|
||||||
pub(crate) use lexiclean::Lexiclean;
|
|
||||||
pub(crate) use libc::EXIT_FAILURE;
|
pub(crate) use libc::EXIT_FAILURE;
|
||||||
pub(crate) use log::info;
|
pub(crate) use log::info;
|
||||||
pub(crate) use regex::Regex;
|
pub(crate) use regex::Regex;
|
||||||
|
@ -29,7 +27,6 @@ pub(crate) use structopt::StructOpt;
|
||||||
pub(crate) use strum::VariantNames;
|
pub(crate) use strum::VariantNames;
|
||||||
pub(crate) use strum_macros::{EnumVariantNames, IntoStaticStr};
|
pub(crate) use strum_macros::{EnumVariantNames, IntoStaticStr};
|
||||||
pub(crate) use url::Url;
|
pub(crate) use url::Url;
|
||||||
pub(crate) use walkdir::WalkDir;
|
|
||||||
|
|
||||||
// modules
|
// modules
|
||||||
pub(crate) use crate::error;
|
pub(crate) use crate::error;
|
||||||
|
@ -39,9 +36,9 @@ pub(crate) use crate::{command_ext::CommandExt, row::Row, slug::Slug, template_e
|
||||||
|
|
||||||
// structs and enums
|
// structs and enums
|
||||||
pub(crate) use crate::{
|
pub(crate) use crate::{
|
||||||
bin::Bin, changelog::Changelog, config::Config, entry::Entry, error::Error, example::Example,
|
arguments::Arguments, bin::Bin, bin_subcommand::BinSubcommand, changelog::Changelog,
|
||||||
faq::Faq, faq_entry::FaqEntry, introduction::Introduction, kind::Kind, metadata::Metadata,
|
config::Config, entry::Entry, error::Error, example::Example, faq::Faq, faq_entry::FaqEntry,
|
||||||
opt::Opt, package::Package, project::Project, readme::Readme, reference::Reference,
|
introduction::Introduction, kind::Kind, metadata::Metadata, options::Options, package::Package,
|
||||||
reference_section::ReferenceSection, release::Release, subcommand::Subcommand, summary::Summary,
|
project::Project, readme::Readme, reference::Reference, reference_section::ReferenceSection,
|
||||||
table::Table,
|
release::Release, subcommand::Subcommand, summary::Summary, table::Table,
|
||||||
};
|
};
|
||||||
|
|
|
@ -58,6 +58,12 @@ pub(crate) enum Error {
|
||||||
dst: PathBuf,
|
dst: PathBuf,
|
||||||
source: io::Error,
|
source: io::Error,
|
||||||
},
|
},
|
||||||
|
#[snafu(display("I/O error copying `{}` to `{}`: {}", src.display(), dst.display(), source))]
|
||||||
|
FilesystemRecursiveCopy {
|
||||||
|
src: PathBuf,
|
||||||
|
dst: PathBuf,
|
||||||
|
source: fs_extra::error::Error,
|
||||||
|
},
|
||||||
#[snafu(display("Git error: {}", source))]
|
#[snafu(display("Git error: {}", source))]
|
||||||
Git { source: git2::Error },
|
Git { source: git2::Error },
|
||||||
#[snafu(display("Regex compilation error: {}", source))]
|
#[snafu(display("Regex compilation error: {}", source))]
|
||||||
|
@ -73,10 +79,6 @@ pub(crate) enum Error {
|
||||||
TemplateRender { source: askama::Error },
|
TemplateRender { source: askama::Error },
|
||||||
#[snafu(display("Failed to get workdir for repo at `{}`", repo.display()))]
|
#[snafu(display("Failed to get workdir for repo at `{}`", repo.display()))]
|
||||||
Workdir { repo: PathBuf },
|
Workdir { repo: PathBuf },
|
||||||
#[snafu(display("Failed to build overrides: {}", source))]
|
|
||||||
Ignore { source: ignore::Error },
|
|
||||||
#[snafu(display("Failed to traverse worktree: {}", source))]
|
|
||||||
Walkdir { source: walkdir::Error },
|
|
||||||
#[snafu(display("Failed to strip path prefix: {}", source))]
|
#[snafu(display("Failed to strip path prefix: {}", source))]
|
||||||
StripPrefix { source: StripPrefixError },
|
StripPrefix { source: StripPrefixError },
|
||||||
}
|
}
|
||||||
|
@ -98,15 +100,3 @@ impl From<cargo_toml::Error> for Error {
|
||||||
Self::CargoToml { source }
|
Self::CargoToml { source }
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
impl From<ignore::Error> for Error {
|
|
||||||
fn from(source: ignore::Error) -> Self {
|
|
||||||
Self::Ignore { source }
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
impl From<walkdir::Error> for Error {
|
|
||||||
fn from(source: walkdir::Error) -> Self {
|
|
||||||
Self::Walkdir { source }
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
|
@ -3,7 +3,9 @@ use crate::common::*;
|
||||||
#[macro_use]
|
#[macro_use]
|
||||||
mod cmd;
|
mod cmd;
|
||||||
|
|
||||||
|
mod arguments;
|
||||||
mod bin;
|
mod bin;
|
||||||
|
mod bin_subcommand;
|
||||||
mod changelog;
|
mod changelog;
|
||||||
mod command_ext;
|
mod command_ext;
|
||||||
mod common;
|
mod common;
|
||||||
|
@ -16,7 +18,7 @@ mod faq_entry;
|
||||||
mod introduction;
|
mod introduction;
|
||||||
mod kind;
|
mod kind;
|
||||||
mod metadata;
|
mod metadata;
|
||||||
mod opt;
|
mod options;
|
||||||
mod package;
|
mod package;
|
||||||
mod project;
|
mod project;
|
||||||
mod readme;
|
mod readme;
|
||||||
|
@ -33,7 +35,7 @@ mod template_ext;
|
||||||
fn main() {
|
fn main() {
|
||||||
pretty_env_logger::init();
|
pretty_env_logger::init();
|
||||||
|
|
||||||
if let Err(error) = Opt::from_args().run() {
|
if let Err(error) = Arguments::from_args().run() {
|
||||||
let bold = Style::new().bold();
|
let bold = Style::new().bold();
|
||||||
let red = Style::new().fg(ansi_term::Color::Red).bold();
|
let red = Style::new().fg(ansi_term::Color::Red).bold();
|
||||||
eprintln!("{}: {}", red.paint("error"), bold.paint(error.to_string()));
|
eprintln!("{}: {}", red.paint("error"), bold.paint(error.to_string()));
|
||||||
|
|
|
@ -1,283 +0,0 @@
|
||||||
use crate::common::*;
|
|
||||||
|
|
||||||
#[derive(StructOpt)]
|
|
||||||
pub(crate) enum Opt {
|
|
||||||
#[structopt(about("Update all generated docs"))]
|
|
||||||
All,
|
|
||||||
#[structopt(about("Generate book"))]
|
|
||||||
Book,
|
|
||||||
#[structopt(about("Generate the changelog"))]
|
|
||||||
Changelog,
|
|
||||||
#[structopt(about("Print a commit template to standard output"))]
|
|
||||||
CommitTemplate,
|
|
||||||
#[structopt(about("Print possible values for `type` field of commit metadata"))]
|
|
||||||
CommitTypes,
|
|
||||||
#[structopt(about("Generate completion scripts"))]
|
|
||||||
CompletionScripts,
|
|
||||||
#[structopt(about("Diff generated content between commits"))]
|
|
||||||
Diff,
|
|
||||||
#[structopt(about("Generate readme"))]
|
|
||||||
Readme,
|
|
||||||
#[structopt(about("Generate man pages"))]
|
|
||||||
Man,
|
|
||||||
}
|
|
||||||
|
|
||||||
#[throws]
|
|
||||||
fn blank(path: impl AsRef<Path>, title: &str) {
|
|
||||||
let path = path.as_ref();
|
|
||||||
|
|
||||||
info!("Writing blank page to `{}`…", path.display());
|
|
||||||
|
|
||||||
let text = format!(
|
|
||||||
"
|
|
||||||
# {}
|
|
||||||
|
|
||||||
This page intentionally left blank.
|
|
||||||
",
|
|
||||||
title
|
|
||||||
);
|
|
||||||
|
|
||||||
fs::write(&path, text).context(error::Filesystem { path })?;
|
|
||||||
}
|
|
||||||
|
|
||||||
#[throws]
|
|
||||||
fn clean_dir(path: impl AsRef<Path>) {
|
|
||||||
let path = path.as_ref();
|
|
||||||
|
|
||||||
info!("Cleaning `{}`…", path.display());
|
|
||||||
|
|
||||||
if path.is_dir() {
|
|
||||||
fs::remove_dir_all(path).context(error::Filesystem { path: &path })?;
|
|
||||||
}
|
|
||||||
|
|
||||||
fs::create_dir_all(path).context(error::Filesystem { path: &path })?;
|
|
||||||
}
|
|
||||||
|
|
||||||
impl Opt {
|
|
||||||
#[throws]
|
|
||||||
pub(crate) fn run(self) {
|
|
||||||
let project = Project::load()?;
|
|
||||||
|
|
||||||
match self {
|
|
||||||
Self::Changelog => Self::changelog(&project)?,
|
|
||||||
Self::CommitTemplate => {
|
|
||||||
println!("{}", Metadata::default().to_string());
|
|
||||||
}
|
|
||||||
Self::CommitTypes => {
|
|
||||||
for kind in Kind::VARIANTS {
|
|
||||||
println!("{}", kind)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
Self::CompletionScripts => Self::completion_scripts(&project)?,
|
|
||||||
Self::Readme => Self::readme(&project)?,
|
|
||||||
Self::Book => Self::book(&project)?,
|
|
||||||
Self::Man => Self::man(&project)?,
|
|
||||||
Self::Diff => Self::diff(&project)?,
|
|
||||||
Self::All => Self::all(&project)?,
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
#[throws]
|
|
||||||
pub(crate) fn all(project: &Project) {
|
|
||||||
Self::changelog(&project)?;
|
|
||||||
Self::completion_scripts(&project)?;
|
|
||||||
Self::readme(&project)?;
|
|
||||||
Self::book(&project)?;
|
|
||||||
Self::man(&project)?;
|
|
||||||
}
|
|
||||||
|
|
||||||
#[throws]
|
|
||||||
pub(crate) fn changelog(project: &Project) {
|
|
||||||
info!("Generating changelog…");
|
|
||||||
let changelog = Changelog::new(&project)?;
|
|
||||||
|
|
||||||
let path = project.root.join("CHANGELOG.md");
|
|
||||||
|
|
||||||
fs::write(&path, changelog.render(false)?).context(error::Filesystem { path })?;
|
|
||||||
}
|
|
||||||
|
|
||||||
#[throws]
|
|
||||||
pub(crate) fn completion_scripts(project: &Project) {
|
|
||||||
info!("Generating completion scripts…");
|
|
||||||
let completions = project.root.join("completions");
|
|
||||||
|
|
||||||
clean_dir(&completions)?;
|
|
||||||
|
|
||||||
cmd!(
|
|
||||||
"cargo",
|
|
||||||
"run",
|
|
||||||
"--package",
|
|
||||||
"imdl",
|
|
||||||
"completions",
|
|
||||||
"--dir",
|
|
||||||
completions
|
|
||||||
)
|
|
||||||
.status_into_result()?
|
|
||||||
}
|
|
||||||
|
|
||||||
#[throws]
|
|
||||||
pub(crate) fn diff(project: &Project) {
|
|
||||||
let tmp = tempfile::tempdir().context(error::Tempdir)?;
|
|
||||||
|
|
||||||
let generated = &[
|
|
||||||
"/CHANGELOG.md",
|
|
||||||
"/README.md",
|
|
||||||
"/book/src/SUMMARY.md",
|
|
||||||
"/book/src/bittorrent.md",
|
|
||||||
"/book/src/changelog.md",
|
|
||||||
"/book/src/commands.md",
|
|
||||||
"/book/src/commands/*",
|
|
||||||
"/book/src/faq.md",
|
|
||||||
"/book/src/introduction.md",
|
|
||||||
"/book/src/references.md",
|
|
||||||
"/book/src/references/*",
|
|
||||||
"/completions/*",
|
|
||||||
"/man/*",
|
|
||||||
];
|
|
||||||
|
|
||||||
let gen = |name: &str| -> Result<(), Error> {
|
|
||||||
cmd!("cargo", "run", "--package", "gen", "all").status_into_result()?;
|
|
||||||
|
|
||||||
let dir = tmp.path().join(name);
|
|
||||||
|
|
||||||
fs::create_dir(&dir).context(error::Filesystem { path: &dir })?;
|
|
||||||
|
|
||||||
let mut builder = OverrideBuilder::new(&project.root);
|
|
||||||
|
|
||||||
for pattern in generated {
|
|
||||||
builder.add(pattern)?;
|
|
||||||
}
|
|
||||||
|
|
||||||
let overrides = builder.build()?;
|
|
||||||
|
|
||||||
for result in WalkDir::new(&project.root) {
|
|
||||||
let entry = result?;
|
|
||||||
|
|
||||||
let src = entry.path();
|
|
||||||
|
|
||||||
if src.is_dir() {
|
|
||||||
continue;
|
|
||||||
}
|
|
||||||
|
|
||||||
if !overrides.matched(&src, false).is_whitelist() {
|
|
||||||
continue;
|
|
||||||
}
|
|
||||||
|
|
||||||
let relative = src
|
|
||||||
.strip_prefix(&project.root)
|
|
||||||
.context(error::StripPrefix)?;
|
|
||||||
|
|
||||||
let dst = dir.join(relative);
|
|
||||||
|
|
||||||
let dst_dir = dst.join("..").lexiclean();
|
|
||||||
|
|
||||||
fs::create_dir_all(&dst_dir).context(error::Filesystem { path: dst_dir })?;
|
|
||||||
|
|
||||||
fs::copy(&src, &dst).context(error::FilesystemCopy { src, dst })?;
|
|
||||||
}
|
|
||||||
|
|
||||||
Ok(())
|
|
||||||
};
|
|
||||||
|
|
||||||
const HEAD: &str = "HEAD";
|
|
||||||
|
|
||||||
gen(HEAD)?;
|
|
||||||
|
|
||||||
let head = project.repo.head()?;
|
|
||||||
|
|
||||||
let head_commit = head.peel_to_commit()?;
|
|
||||||
|
|
||||||
let parent = head_commit.parent(0)?;
|
|
||||||
|
|
||||||
let parent_hash = parent.id().to_string();
|
|
||||||
|
|
||||||
cmd!("git", "checkout", &parent_hash).status_into_result()?;
|
|
||||||
|
|
||||||
gen(&parent_hash)?;
|
|
||||||
|
|
||||||
cmd!("colordiff", "-ur", parent_hash, HEAD)
|
|
||||||
.current_dir(tmp.path())
|
|
||||||
.status_into_result()
|
|
||||||
.ok();
|
|
||||||
|
|
||||||
cmd!(
|
|
||||||
"git",
|
|
||||||
"checkout",
|
|
||||||
head
|
|
||||||
.shorthand()
|
|
||||||
.map(str::to_owned)
|
|
||||||
.unwrap_or_else(|| head_commit.id().to_string())
|
|
||||||
)
|
|
||||||
.status_into_result()?;
|
|
||||||
}
|
|
||||||
|
|
||||||
#[throws]
|
|
||||||
pub(crate) fn readme(project: &Project) {
|
|
||||||
info!("Generating readme…");
|
|
||||||
|
|
||||||
let template = project.root.join("bin/gen/templates/README.md");
|
|
||||||
|
|
||||||
let readme = Readme::load(&project.config, &template)?;
|
|
||||||
|
|
||||||
let text = readme.render_newline()?;
|
|
||||||
|
|
||||||
let path = project.root.join("README.md");
|
|
||||||
fs::write(&path, text).context(error::Filesystem { path })?;
|
|
||||||
}
|
|
||||||
|
|
||||||
#[throws]
|
|
||||||
pub(crate) fn book(project: &Project) {
|
|
||||||
info!("Generating book…");
|
|
||||||
|
|
||||||
blank(project.root.join("book/src/commands.md"), "Commands")?;
|
|
||||||
blank(project.root.join("book/src/bittorrent.md"), "BitTorrent")?;
|
|
||||||
blank(project.root.join("book/src/references.md"), "References")?;
|
|
||||||
|
|
||||||
let commands = project.root.join("book/src/commands/");
|
|
||||||
|
|
||||||
clean_dir(&commands)?;
|
|
||||||
|
|
||||||
for subcommand in &project.bin.subcommands {
|
|
||||||
let page = subcommand.page()?;
|
|
||||||
|
|
||||||
let dst = commands.join(format!("{}.md", subcommand.slug()));
|
|
||||||
|
|
||||||
fs::write(&dst, page).context(error::Filesystem { path: dst })?;
|
|
||||||
}
|
|
||||||
|
|
||||||
clean_dir(&project.root.join("book/src/references/"))?;
|
|
||||||
|
|
||||||
for section in &project.config.references {
|
|
||||||
section.render_to(project.root.join("book/src").join(section.path()))?;
|
|
||||||
}
|
|
||||||
|
|
||||||
Faq::new(&project.config.faq).render_to(project.root.join("book/src/faq.md"))?;
|
|
||||||
|
|
||||||
Summary::new(project).render_to(project.root.join("book/src/SUMMARY.md"))?;
|
|
||||||
|
|
||||||
Introduction::new(&project.config).render_to(project.root.join("book/src/introduction.md"))?;
|
|
||||||
|
|
||||||
let changelog = Changelog::new(&project)?;
|
|
||||||
|
|
||||||
let dst = project.root.join("book/src/changelog.md");
|
|
||||||
fs::write(&dst, changelog.render(true)?).context(error::Filesystem { path: dst })?;
|
|
||||||
}
|
|
||||||
|
|
||||||
#[throws]
|
|
||||||
pub(crate) fn man(project: &Project) {
|
|
||||||
info!("Generating man pages…");
|
|
||||||
let mans = project.root.join("man");
|
|
||||||
|
|
||||||
clean_dir(&mans)?;
|
|
||||||
|
|
||||||
for subcommand in &project.bin.subcommands {
|
|
||||||
let man = subcommand.man()?;
|
|
||||||
|
|
||||||
let dst = mans.join(format!("{}.1", subcommand.slug()));
|
|
||||||
|
|
||||||
info!("Writing man page to `{}`", dst.display());
|
|
||||||
|
|
||||||
fs::write(&dst, man).context(error::Filesystem { path: dst })?;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
11
bin/gen/src/options.rs
Normal file
11
bin/gen/src/options.rs
Normal file
|
@ -0,0 +1,11 @@
|
||||||
|
use crate::common::*;
|
||||||
|
|
||||||
|
#[derive(StructOpt)]
|
||||||
|
pub(crate) struct Options {
|
||||||
|
#[structopt(
|
||||||
|
long("bin"),
|
||||||
|
value_name("EXECUTABLE"),
|
||||||
|
help("Path to the `imdl` binary.")
|
||||||
|
)]
|
||||||
|
pub(crate) bin: PathBuf,
|
||||||
|
}
|
|
@ -5,11 +5,12 @@ pub(crate) struct Project {
|
||||||
pub(crate) root: PathBuf,
|
pub(crate) root: PathBuf,
|
||||||
pub(crate) config: Config,
|
pub(crate) config: Config,
|
||||||
pub(crate) bin: Bin,
|
pub(crate) bin: Bin,
|
||||||
|
pub(crate) executable: PathBuf,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl Project {
|
impl Project {
|
||||||
#[throws]
|
#[throws]
|
||||||
pub(crate) fn load() -> Self {
|
pub(crate) fn load(options: &Options) -> Self {
|
||||||
let start_dir = env::current_dir().context(error::CurrentDir)?;
|
let start_dir = env::current_dir().context(error::CurrentDir)?;
|
||||||
|
|
||||||
let repo = Repository::discover(&start_dir).context(error::RepositoryDiscover { start_dir })?;
|
let repo = Repository::discover(&start_dir).context(error::RepositoryDiscover { start_dir })?;
|
||||||
|
@ -23,7 +24,7 @@ impl Project {
|
||||||
|
|
||||||
let config = Config::load(&root)?;
|
let config = Config::load(&root)?;
|
||||||
|
|
||||||
let bin = Bin::new(&root.join("target/debug/imdl"))?;
|
let bin = Bin::new(&options.bin)?;
|
||||||
|
|
||||||
let example_commands = config
|
let example_commands = config
|
||||||
.examples
|
.examples
|
||||||
|
@ -45,10 +46,22 @@ impl Project {
|
||||||
}
|
}
|
||||||
|
|
||||||
Project {
|
Project {
|
||||||
|
executable: options.bin.clone(),
|
||||||
|
bin,
|
||||||
|
config,
|
||||||
repo,
|
repo,
|
||||||
root,
|
root,
|
||||||
config,
|
|
||||||
bin,
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[throws]
|
||||||
|
pub(crate) fn gen(&self) -> PathBuf {
|
||||||
|
let gen = self.root.join("target").join("gen");
|
||||||
|
|
||||||
|
if !gen.is_dir() {
|
||||||
|
fs::create_dir_all(&gen).context(error::Filesystem { path: &gen })?;
|
||||||
|
}
|
||||||
|
|
||||||
|
gen
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -9,6 +9,6 @@ pub(crate) struct ReferenceSection {
|
||||||
|
|
||||||
impl ReferenceSection {
|
impl ReferenceSection {
|
||||||
pub(crate) fn path(&self) -> String {
|
pub(crate) fn path(&self) -> String {
|
||||||
format!("./references/{}.md", self.title.slug())
|
format!("references/{}.md", self.title.slug())
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,164 +1,240 @@
|
||||||
use crate::common::*;
|
use crate::common::*;
|
||||||
|
|
||||||
#[derive(Debug, Ord, PartialOrd, Eq, PartialEq)]
|
#[derive(StructOpt)]
|
||||||
pub(crate) struct Subcommand {
|
pub(crate) enum Subcommand {
|
||||||
pub(crate) bin: PathBuf,
|
#[structopt(about("Update all generated docs"))]
|
||||||
pub(crate) command: Vec<String>,
|
All,
|
||||||
pub(crate) subcommands: Vec<String>,
|
#[structopt(about("Generate book"))]
|
||||||
|
Book,
|
||||||
|
#[structopt(about("Generate the changelog"))]
|
||||||
|
Changelog,
|
||||||
|
#[structopt(about("Print a commit template to standard output"))]
|
||||||
|
CommitTemplate,
|
||||||
|
#[structopt(about("Print possible values for `type` field of commit metadata"))]
|
||||||
|
CommitTypes,
|
||||||
|
#[structopt(about("Generate completion scripts"))]
|
||||||
|
CompletionScripts,
|
||||||
|
#[structopt(about("Diff generated content between commits"))]
|
||||||
|
Diff,
|
||||||
|
#[structopt(about("Generate readme"))]
|
||||||
|
Readme,
|
||||||
|
#[structopt(about("Generate man pages"))]
|
||||||
|
Man,
|
||||||
|
}
|
||||||
|
|
||||||
|
#[throws]
|
||||||
|
fn blank(path: impl AsRef<Path>, title: &str) {
|
||||||
|
let path = path.as_ref();
|
||||||
|
|
||||||
|
info!("Writing blank page to `{}`…", path.display());
|
||||||
|
|
||||||
|
let text = format!(
|
||||||
|
"
|
||||||
|
# {}
|
||||||
|
|
||||||
|
This page intentionally left blank.
|
||||||
|
",
|
||||||
|
title
|
||||||
|
);
|
||||||
|
|
||||||
|
fs::write(&path, text).context(error::Filesystem { path })?;
|
||||||
|
}
|
||||||
|
|
||||||
|
#[throws]
|
||||||
|
fn clean_dir(path: impl AsRef<Path>) {
|
||||||
|
let path = path.as_ref();
|
||||||
|
|
||||||
|
info!("Cleaning `{}`…", path.display());
|
||||||
|
|
||||||
|
if path.is_dir() {
|
||||||
|
fs::remove_dir_all(path).context(error::Filesystem { path: &path })?;
|
||||||
|
}
|
||||||
|
|
||||||
|
fs::create_dir_all(path).context(error::Filesystem { path: &path })?;
|
||||||
}
|
}
|
||||||
|
|
||||||
impl Subcommand {
|
impl Subcommand {
|
||||||
#[throws]
|
#[throws]
|
||||||
pub(crate) fn new(bin: &Path, command: Vec<String>) -> Self {
|
pub(crate) fn run(self, options: Options) {
|
||||||
let wide_help = Command::new(bin)
|
let project = Project::load(&options)?;
|
||||||
.args(command.as_slice())
|
|
||||||
.env("IMDL_TERM_WIDTH", "200")
|
|
||||||
.arg("--help")
|
|
||||||
.out()?;
|
|
||||||
|
|
||||||
const MARKER: &str = "\nSUBCOMMANDS:\n";
|
match self {
|
||||||
|
Self::Changelog => Self::changelog(&project)?,
|
||||||
let mut subcommands = Vec::new();
|
Self::CommitTemplate => {
|
||||||
|
println!("{}", Metadata::default().to_string());
|
||||||
if let Some(marker) = wide_help.find(MARKER) {
|
|
||||||
let block = &wide_help[marker + MARKER.len()..];
|
|
||||||
|
|
||||||
for line in block.lines() {
|
|
||||||
let name = line.trim().split_whitespace().next().unwrap();
|
|
||||||
subcommands.push(name.into());
|
|
||||||
}
|
}
|
||||||
}
|
Self::CommitTypes => {
|
||||||
|
for kind in Kind::VARIANTS {
|
||||||
Self {
|
println!("{}", kind)
|
||||||
bin: bin.into(),
|
}
|
||||||
command,
|
}
|
||||||
subcommands,
|
Self::CompletionScripts => Self::completion_scripts(&project)?,
|
||||||
|
Self::Readme => Self::readme(&project)?,
|
||||||
|
Self::Book => Self::book(&project)?,
|
||||||
|
Self::Man => Self::man(&project)?,
|
||||||
|
Self::Diff => Self::diff(&project)?,
|
||||||
|
Self::All => Self::all(&project)?,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
#[throws]
|
#[throws]
|
||||||
fn help(&self) -> String {
|
pub(crate) fn all(project: &Project) {
|
||||||
info!("Getting help for `{}`", self.command_line());
|
Self::changelog(&project)?;
|
||||||
|
Self::completion_scripts(&project)?;
|
||||||
Command::new(&self.bin)
|
Self::readme(&project)?;
|
||||||
.args(self.command.as_slice())
|
Self::book(&project)?;
|
||||||
.env("IMDL_TERM_WIDTH", "80")
|
Self::man(&project)?;
|
||||||
.arg("--help")
|
|
||||||
.out()?
|
|
||||||
}
|
}
|
||||||
|
|
||||||
#[throws]
|
#[throws]
|
||||||
pub(crate) fn man(&self) -> String {
|
pub(crate) fn changelog(project: &Project) {
|
||||||
let command_line = self.command_line();
|
info!("Generating changelog…");
|
||||||
|
let changelog = Changelog::new(&project)?;
|
||||||
|
|
||||||
info!("Generating man page for `{}`", command_line);
|
let path = project.gen()?.join("CHANGELOG.md");
|
||||||
|
|
||||||
let name = command_line.replace(" ", "\\ ");
|
fs::write(&path, changelog.render(false)?).context(error::Filesystem { path })?;
|
||||||
|
}
|
||||||
|
|
||||||
let help = self.help()?;
|
#[throws]
|
||||||
|
pub(crate) fn completion_scripts(project: &Project) {
|
||||||
|
info!("Generating completion scripts…");
|
||||||
|
let completions = project.gen()?.join("completions");
|
||||||
|
|
||||||
let description = if self.command.is_empty() {
|
clean_dir(&completions)?;
|
||||||
"A 40' shipping container for the Internet".to_string()
|
|
||||||
} else {
|
|
||||||
help.lines().nth(1).unwrap().into()
|
|
||||||
};
|
|
||||||
|
|
||||||
let include = format!(
|
cmd!(&project.executable, "completions", "--dir", completions).status_into_result()?
|
||||||
"\
|
}
|
||||||
[NAME]
|
|
||||||
\\fB{}\\fR
|
|
||||||
- {}
|
|
||||||
",
|
|
||||||
name, description
|
|
||||||
);
|
|
||||||
|
|
||||||
|
#[throws]
|
||||||
|
pub(crate) fn diff(project: &Project) {
|
||||||
let tmp = tempfile::tempdir().context(error::Tempdir)?;
|
let tmp = tempfile::tempdir().context(error::Tempdir)?;
|
||||||
|
|
||||||
let include_path = tmp.path().join("include");
|
let gen = |name: &str| -> Result<(), Error> {
|
||||||
|
let src = Path::new("target/gen");
|
||||||
|
|
||||||
fs::write(&include_path, include).context(error::Filesystem {
|
fs::remove_dir_all(src).context(error::Filesystem { path: src })?;
|
||||||
path: &include_path,
|
|
||||||
})?;
|
|
||||||
|
|
||||||
let version = cmd!(&self.bin, "--version")
|
cmd!("cargo", "run", "--package", "gen", "all").status_into_result()?;
|
||||||
.out()?
|
|
||||||
.split_whitespace()
|
|
||||||
.nth(1)
|
|
||||||
.unwrap()
|
|
||||||
.to_owned();
|
|
||||||
|
|
||||||
info!("Running help2man for `{}`", command_line);
|
let dir = tmp.path().join(name);
|
||||||
|
|
||||||
let mut command = self.bin.as_os_str().to_owned();
|
fs::create_dir(&dir).context(error::Filesystem { path: &dir })?;
|
||||||
for arg in &self.command {
|
|
||||||
command.push(" ");
|
|
||||||
command.push(arg);
|
|
||||||
}
|
|
||||||
|
|
||||||
let output = cmd!(
|
fs_extra::dir::copy(src, &dir, &fs_extra::dir::CopyOptions::new())
|
||||||
"help2man",
|
.context(error::FilesystemRecursiveCopy { src, dst: dir })?;
|
||||||
"--include",
|
|
||||||
&include_path,
|
Ok(())
|
||||||
"--manual",
|
};
|
||||||
"Intermodal Manual",
|
|
||||||
"--no-info",
|
const HEAD: &str = "HEAD";
|
||||||
"--source",
|
|
||||||
&format!("Intermodal {}", version),
|
gen(HEAD)?;
|
||||||
command
|
|
||||||
|
let head = project.repo.head()?;
|
||||||
|
|
||||||
|
let head_commit = head.peel_to_commit()?;
|
||||||
|
|
||||||
|
let parent = head_commit.parent(0)?;
|
||||||
|
|
||||||
|
let parent_hash = parent.id().to_string();
|
||||||
|
|
||||||
|
cmd!("git", "checkout", &parent_hash).status_into_result()?;
|
||||||
|
|
||||||
|
gen(&parent_hash)?;
|
||||||
|
|
||||||
|
cmd!("colordiff", "-ur", parent_hash, HEAD)
|
||||||
|
.current_dir(tmp.path())
|
||||||
|
.status_into_result()
|
||||||
|
.ok();
|
||||||
|
|
||||||
|
cmd!(
|
||||||
|
"git",
|
||||||
|
"checkout",
|
||||||
|
head
|
||||||
|
.shorthand()
|
||||||
|
.map(str::to_owned)
|
||||||
|
.unwrap_or_else(|| head_commit.id().to_string())
|
||||||
)
|
)
|
||||||
.out()?;
|
.status_into_result()?;
|
||||||
|
|
||||||
let man = output
|
|
||||||
.replace("📦 ", "\n")
|
|
||||||
.replace("\n.SS ", "\n.SH ")
|
|
||||||
.replace("\"USAGE:\"", "\"SYNOPSIS:\"");
|
|
||||||
|
|
||||||
let re = Regex::new(r"(?ms).SH DESCRIPTION.*?.SH").unwrap();
|
|
||||||
|
|
||||||
let man = re.replace(&man, ".SH").into_owned();
|
|
||||||
|
|
||||||
man
|
|
||||||
}
|
|
||||||
|
|
||||||
pub(crate) fn slug(&self) -> String {
|
|
||||||
let mut slug = self
|
|
||||||
.bin
|
|
||||||
.display()
|
|
||||||
.to_string()
|
|
||||||
.split('/')
|
|
||||||
.last()
|
|
||||||
.unwrap()
|
|
||||||
.to_owned();
|
|
||||||
|
|
||||||
for name in &self.command {
|
|
||||||
slug.push('-');
|
|
||||||
slug.push_str(&name);
|
|
||||||
}
|
|
||||||
|
|
||||||
slug
|
|
||||||
}
|
|
||||||
|
|
||||||
pub(crate) fn command_line(&self) -> String {
|
|
||||||
let mut line = self
|
|
||||||
.bin
|
|
||||||
.display()
|
|
||||||
.to_string()
|
|
||||||
.split('/')
|
|
||||||
.last()
|
|
||||||
.unwrap()
|
|
||||||
.to_owned();
|
|
||||||
|
|
||||||
for name in &self.command {
|
|
||||||
line.push(' ');
|
|
||||||
line.push_str(&name);
|
|
||||||
}
|
|
||||||
|
|
||||||
line
|
|
||||||
}
|
}
|
||||||
|
|
||||||
#[throws]
|
#[throws]
|
||||||
pub(crate) fn page(&self) -> String {
|
pub(crate) fn readme(project: &Project) {
|
||||||
let help = self.help()?;
|
info!("Generating readme…");
|
||||||
format!("# `{}`\n```\n{}\n```", self.command_line(), help)
|
|
||||||
|
let template = project.root.join("bin/gen/templates/README.md");
|
||||||
|
|
||||||
|
let readme = Readme::load(&project.config, &template)?;
|
||||||
|
|
||||||
|
let text = readme.render_newline()?;
|
||||||
|
|
||||||
|
let path = project.gen()?.join("README.md");
|
||||||
|
fs::write(&path, &text).context(error::Filesystem { path })?;
|
||||||
|
|
||||||
|
let path = project.root.join("README.md");
|
||||||
|
fs::write(&path, &text).context(error::Filesystem { path })?;
|
||||||
|
}
|
||||||
|
|
||||||
|
#[throws]
|
||||||
|
pub(crate) fn book(project: &Project) {
|
||||||
|
info!("Generating book…");
|
||||||
|
|
||||||
|
let gen = project.gen()?;
|
||||||
|
|
||||||
|
let out = gen.join("book");
|
||||||
|
|
||||||
|
fs::create_dir_all(&out).context(error::Filesystem { path: &out })?;
|
||||||
|
|
||||||
|
blank(out.join("commands.md"), "Commands")?;
|
||||||
|
blank(out.join("bittorrent.md"), "BitTorrent")?;
|
||||||
|
blank(out.join("references.md"), "References")?;
|
||||||
|
|
||||||
|
let commands = out.join("commands");
|
||||||
|
|
||||||
|
clean_dir(&commands)?;
|
||||||
|
|
||||||
|
for subcommand in &project.bin.subcommands {
|
||||||
|
let page = subcommand.page()?;
|
||||||
|
|
||||||
|
let dst = commands.join(format!("{}.md", subcommand.slug()));
|
||||||
|
|
||||||
|
fs::write(&dst, page).context(error::Filesystem { path: dst })?;
|
||||||
|
}
|
||||||
|
|
||||||
|
clean_dir(&out.join("references"))?;
|
||||||
|
|
||||||
|
for section in &project.config.references {
|
||||||
|
section.render_to(out.join(section.path()))?;
|
||||||
|
}
|
||||||
|
|
||||||
|
Faq::new(&project.config.faq).render_to(out.join("faq.md"))?;
|
||||||
|
|
||||||
|
Summary::new(project).render_to(out.join("SUMMARY.md"))?;
|
||||||
|
|
||||||
|
Introduction::new(&project.config).render_to(out.join("introduction.md"))?;
|
||||||
|
|
||||||
|
let changelog = Changelog::new(&project)?;
|
||||||
|
|
||||||
|
let dst = out.join("changelog.md");
|
||||||
|
fs::write(&dst, changelog.render(true)?).context(error::Filesystem { path: dst })?;
|
||||||
|
}
|
||||||
|
|
||||||
|
#[throws]
|
||||||
|
pub(crate) fn man(project: &Project) {
|
||||||
|
info!("Generating man pages…");
|
||||||
|
let mans = project.gen()?.join("man");
|
||||||
|
|
||||||
|
clean_dir(&mans)?;
|
||||||
|
|
||||||
|
for subcommand in &project.bin.subcommands {
|
||||||
|
let man = subcommand.man()?;
|
||||||
|
|
||||||
|
let dst = mans.join(format!("{}.1", subcommand.slug()));
|
||||||
|
|
||||||
|
info!("Writing man page to `{}`", dst.display());
|
||||||
|
|
||||||
|
fs::write(&dst, man).context(error::Filesystem { path: dst })?;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -154,46 +154,38 @@ Intermodal is distributed under the
|
||||||
a public domain dedication with a fallback all-permissive license. The SPDX
|
a public domain dedication with a fallback all-permissive license. The SPDX
|
||||||
identifier of the CC0 is [CC0-1.0](https://spdx.org/licenses/CC0-1.0.html).
|
identifier of the CC0 is [CC0-1.0](https://spdx.org/licenses/CC0-1.0.html).
|
||||||
|
|
||||||
### Package Artifacts
|
### Build Artifacts
|
||||||
|
|
||||||
There are three primary build artifacts: the binary, the man pages, and the
|
There are a number of build artifacts: the binary, the man pages, the
|
||||||
shell completion scripts.
|
changelog, and the shell completion scripts.
|
||||||
|
|
||||||
#### Binary
|
The binary is built with `cargo`, and the other artifacts are built `gen`,
|
||||||
|
located in `bin/gen`.
|
||||||
|
|
||||||
The binary is called `imdl`, and can be built with:
|
The binary can be built with:
|
||||||
|
|
||||||
```
|
cargo build --release
|
||||||
cargo build --release
|
|
||||||
```
|
|
||||||
|
|
||||||
After building, the binary will be present at `target/release/imdl`.
|
_`gen` requires [`help2man`](https://www.gnu.org/software/help2man/) to be
|
||||||
|
installed, which is used to generate man pages from subcommand `--help`
|
||||||
|
strings._
|
||||||
|
|
||||||
#### Man Pages
|
The rest of the build artifacts can be built with `gen`:
|
||||||
|
|
||||||
Intermodal has a number of subcommands, each of which has a man page. The man
|
cargo run --package gen -- --bin target/release/imdl all
|
||||||
pages are generated from the `--help` text using
|
|
||||||
[`help2man`](https://www.gnu.org/software/help2man/).
|
|
||||||
|
|
||||||
To generate the man pages, ensure `help2man` is available, and run:
|
_The path to the built `imdl` executable should be passed to `gen` with the `--bin` flag._
|
||||||
|
|
||||||
```
|
After running the above commands, the following table shows the location of the
|
||||||
mkdir -p man
|
built artifacts.
|
||||||
cargo run --package gen man
|
|
||||||
```
|
|
||||||
|
|
||||||
After building, the man pages will be available in `man`.
|
| Artifact | Location |
|
||||||
|
|--------------------|----------------------------|
|
||||||
#### Completion Scripts
|
| Binary | `target/release/imdl` |
|
||||||
|
| Man Pages | `target/gen/man/*` |
|
||||||
Completion scripts are available for a number of shells. To generate them, run:
|
| Completion Scripts | `target/gen/completions/*` |
|
||||||
|
| Changelog | `target/gen/CHANGELOG.md` |
|
||||||
```
|
| Readme | `target/gen/README.md` |
|
||||||
mkdir -p completions
|
|
||||||
cargo run --release completions --dir completions
|
|
||||||
```
|
|
||||||
|
|
||||||
After running, the completion scripts will be available in `completions`.
|
|
||||||
|
|
||||||
### Release Updates
|
### Release Updates
|
||||||
|
|
||||||
|
|
31
bin/package
31
bin/package
|
@ -6,7 +6,7 @@ version=${1#"refs/tags/"}
|
||||||
os=$2
|
os=$2
|
||||||
target=$3
|
target=$3
|
||||||
src=`pwd`
|
src=`pwd`
|
||||||
dist=$src/dist
|
dist=$src/target/dist
|
||||||
bin=imdl
|
bin=imdl
|
||||||
|
|
||||||
echo "Packaging $bin $version for $target..."
|
echo "Packaging $bin $version for $target..."
|
||||||
|
@ -38,32 +38,35 @@ case $os in
|
||||||
esac
|
esac
|
||||||
|
|
||||||
echo "Building completions..."
|
echo "Building completions..."
|
||||||
rm -rf completions
|
cargo run --package gen -- --bin $executable completion-scripts
|
||||||
mkdir completions
|
|
||||||
$executable completions --dir completions
|
|
||||||
|
|
||||||
echo "Generating changelog..."
|
echo "Generating changelog..."
|
||||||
cargo run --package gen changelog
|
cargo run --package gen -- --bin $executable changelog
|
||||||
|
|
||||||
echo "Copying release files..."
|
echo "Generating readme..."
|
||||||
mkdir dist
|
cargo run --package gen -- --bin $executable readme
|
||||||
|
|
||||||
|
echo "Copying static files..."
|
||||||
|
mkdir $dist
|
||||||
cp -r \
|
cp -r \
|
||||||
$executable \
|
$executable \
|
||||||
CHANGELOG.md \
|
|
||||||
CONTRIBUTING \
|
CONTRIBUTING \
|
||||||
Cargo.lock \
|
Cargo.lock \
|
||||||
Cargo.toml \
|
Cargo.toml \
|
||||||
LICENSE \
|
LICENSE \
|
||||||
README.md \
|
$dist
|
||||||
completions \
|
|
||||||
|
echo "Copying generated files..."
|
||||||
|
cp -r \
|
||||||
|
target/gen/README.md \
|
||||||
|
target/gen/CHANGELOG.md \
|
||||||
|
target/gen/completions \
|
||||||
$dist
|
$dist
|
||||||
|
|
||||||
if [[ $os != windows-latest ]]; then
|
if [[ $os != windows-latest ]]; then
|
||||||
echo "Building man pages..."
|
echo "Building man pages..."
|
||||||
rm -rf man
|
cargo run --package gen -- --bin $executable man
|
||||||
mkdir man
|
cp -r target/gen/man $dist/man
|
||||||
cargo run --package gen man
|
|
||||||
cp -r man $dist/man
|
|
||||||
fi
|
fi
|
||||||
|
|
||||||
cd $dist
|
cd $dist
|
||||||
|
|
1
book/src/SUMMARY.md
Symbolic link
1
book/src/SUMMARY.md
Symbolic link
|
@ -0,0 +1 @@
|
||||||
|
../../target/gen/book/SUMMARY.md
|
1
book/src/bittorrent.md
Symbolic link
1
book/src/bittorrent.md
Symbolic link
|
@ -0,0 +1 @@
|
||||||
|
../../target/gen/book/bittorrent.md
|
1
book/src/changelog.md
Symbolic link
1
book/src/changelog.md
Symbolic link
|
@ -0,0 +1 @@
|
||||||
|
../../target/gen/book/changelog.md
|
1
book/src/commands
Symbolic link
1
book/src/commands
Symbolic link
|
@ -0,0 +1 @@
|
||||||
|
../../target/gen/book/commands
|
1
book/src/commands.md
Symbolic link
1
book/src/commands.md
Symbolic link
|
@ -0,0 +1 @@
|
||||||
|
../../target/gen/book/commands.md
|
1
book/src/faq.md
Symbolic link
1
book/src/faq.md
Symbolic link
|
@ -0,0 +1 @@
|
||||||
|
../../target/gen/book/faq.md
|
1
book/src/introduction.md
Symbolic link
1
book/src/introduction.md
Symbolic link
|
@ -0,0 +1 @@
|
||||||
|
../../target/gen/book/introduction.md
|
1
book/src/references
Symbolic link
1
book/src/references
Symbolic link
|
@ -0,0 +1 @@
|
||||||
|
../../target/gen/book/references
|
1
book/src/references.md
Symbolic link
1
book/src/references.md
Symbolic link
|
@ -0,0 +1 @@
|
||||||
|
../../target/gen/book/references.md
|
3
justfile
3
justfile
|
@ -58,7 +58,8 @@ dev-deps:
|
||||||
|
|
||||||
# update generated documentation
|
# update generated documentation
|
||||||
gen:
|
gen:
|
||||||
cargo run --package gen all
|
cargo build
|
||||||
|
cargo run --package gen -- --bin target/debug/imdl all
|
||||||
|
|
||||||
check-minimal-versions:
|
check-minimal-versions:
|
||||||
./bin/check-minimal-versions
|
./bin/check-minimal-versions
|
||||||
|
|
Loading…
Reference in New Issue
Block a user