10 Commits

Author SHA1 Message Date
7a47b73d08 updated cargo lock file
Some checks failed
Rust / build (push) Has been cancelled
2025-10-24 18:05:59 +02:00
a5005d777f incremented version 2025-10-24 18:00:46 +02:00
961fe4ce56 added the .gitlab readme assets to exclude array 2025-10-24 17:07:43 +02:00
fe8e3e3e63 removed binary file 2025-10-23 22:44:44 +02:00
9945b45aa9 incremented version 2025-10-23 22:38:12 +02:00
0713ec0c80 added actions for publishing to crates.io 2025-10-23 22:28:28 +02:00
96d200888a added another notice 2025-10-23 22:17:40 +02:00
13e56f057b updated readme, addded usage and development notices 2025-10-23 22:15:38 +02:00
bb098ce77e updated installation options 2025-10-23 21:14:47 +02:00
4a2f852f30 updated flake. nix package now includes completions 2025-10-23 20:14:05 +02:00
15 changed files with 301 additions and 199 deletions

27
.github/workflows/rust.yml vendored Normal file
View File

@@ -0,0 +1,27 @@
name: Rust
on:
push:
branches: [ "main" ]
pull_request:
branches: [ "main" ]
env:
CARGO_TERM_COLOR: always
jobs:
build:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4
- name: Build
run: cargo build --verbose
- name: Run tests
run: cargo test --verbose
- name: Publish to crates.io
env:
CARGO_REGISTRY_TOKEN: ${{ secrets.CRATES_IO_TOKEN }}
run: |
cargo publish --verbose

1
.gitignore vendored
View File

@@ -3,3 +3,4 @@ dist
/docs
res/test/docs/slides/revealjs
res/test/result
result

4
Cargo.lock generated
View File

@@ -1,6 +1,6 @@
# This file is automatically @generated by Cargo.
# It is not intended for manual editing.
version = 3
version = 4
[[package]]
name = "adler"
@@ -859,7 +859,7 @@ checksum = "212d0f5754cb6769937f4501cc0e67f4f4483c8d2c3e1e922ee9edbe4ab4c7c0"
[[package]]
name = "docki"
version = "1.1.0"
version = "1.2.1"
dependencies = [
"bytes",
"clap 4.1.8",

View File

@@ -1,13 +1,13 @@
# Cargo.toml
build = "build.rs"
[package]
name = "docki"
version = "1.1.0"
version = "1.2.1"
edition = "2021"
description = "cli for building and publishing documentation using asciidoctor"
license-file = "LICENSE.txt"
authors = ["Quirin Ecker"]
exclude = [
".gitlab"
]
# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html
@@ -24,8 +24,4 @@ reqwest = { version = "0.11.14", features = ["blocking"] }
text_io = "0.1.12"
tokio = { version = "1.26.0", features = ["full"] }
zip-extract = "0.1.1"
[build-dependencies]
clap = { version = "4.1.8", features = ["derive"] }
clap_complete = "4.1.4"

View File

@@ -8,24 +8,44 @@
![screencast](.gitlab/screencast.gif)
Docki is cli for converting asciidoctor files into html files. You can build your documentation with `docki build` and write documenation using the live server with `docki serve`.
Docki is cli for converting asciidoctor files into html files.
## Usage
> [!NOTE]
> This works in a directory with a directory called `docs`, which contains the asciidoctor documentation.
### Building the documentation
```shell
docki build
```
### Serving the documentation on a live server
```shell
docki serve
```
## Installation
### Nix
This installation method is recommended, becuase it is the most maintained due to me using nix in development.
This installation method is recommended, because it will include both asciidoctor and asciidoctor_revealjs.
Note: This is the most basic installation. If you are already more experienced, you might want to add it to your shell or home manager configuration.
```nix
```shell
nix profile install github:quirinecker/docki
```
### Homebrew
```
brew tap quirinecker/docki-homebrew https://gitlab.com/quirinecker/docki-homebrew
> [!NOTE]
> Installing it via homebrew will not include asciidoctor_revealjs. It can be installed afterwards with `docki install-reveal`
```shell
brew tap quirinecker/docki-homebrew https://github.com/quirinecker/docki-homebrew
```
```
@@ -34,9 +54,75 @@ brew install docki
### Cargo
> [!NOTE]
> This is the most basic installation. It will not include asciidoctor_revealjs and asciidoctor itself. Installing asciidoctor has to be done manually, while installing asciidoctor_revealjs can be done with `docki install-reveal`
```shell
cargo install docki
```
### Nix (Advanced, Flake)
> [!NOTE]
> There are multiple ways to install docki with nix. This is the way I installed it on my machine.
1. Add it to the flake inputs
```nix
docki = {
url = "github:quirinecker/docki";
inputs.nixpkgs.follows = "nixpkgs";
};
```
2. Add `@inputs` at the end of the outputs (if you haven't already)
```nix
outputs = {
nixpkgs
...
}@inputs:
...Rest of your flake...
```
3. Add the input to your system packages (system configuration) or home packages (home manager configuration)
```nix
environment.systemPackages = with pkgs; [
inputs.docki.packages.${system}.default
]
```
or
```nix
home.packages = with pkgs; [
inputs.docki.packages.${system}.default
]
```
## Development
### Running it
If you just want to run it, you can use the `nix run` command. This will install all the build dependencies, build the binary and run it.
```shell
nix run
```
### Development Shell
You can also use the development shell with the command below. In this shell all dependencies for building and running the project are installed.
```shell
nix develop
```
Afterwards it can be built and run with cargo
```shell
cargo run -- <args>
```

View File

@@ -1,23 +0,0 @@
use std::io::Error;
use clap::CommandFactory;
use clap_complete::{generate_to, shells::{Bash, Zsh, Fish}};
include!("src/app/args/structure.rs");
include!("src/app/fs_util/mod.rs");
fn main() -> Result<(), Error> {
generate_completions()
}
fn generate_completions() -> Result<(), Error> {
let mut command = Args::command();
let home_path = env::var("HOME").expect("could not get home path");
let out_dir = format!("{}/.docki/completions/", home_path);
create_dir_recursive(&out_dir);
generate_to(Bash, &mut command, "docki", &out_dir)?;
generate_to(Zsh, &mut command, "docki", &out_dir)?;
generate_to(Fish, &mut command, "docki", &out_dir)?;
Ok(())
}

BIN
docki

Binary file not shown.

77
flake.lock generated
View File

@@ -1,6 +1,63 @@
{
"nodes": {
"fenix": {
"inputs": {
"nixpkgs": [
"naersk",
"nixpkgs"
],
"rust-analyzer-src": "rust-analyzer-src"
},
"locked": {
"lastModified": 1752475459,
"narHash": "sha256-z6QEu4ZFuHiqdOPbYss4/Q8B0BFhacR8ts6jO/F/aOU=",
"owner": "nix-community",
"repo": "fenix",
"rev": "bf0d6f70f4c9a9cf8845f992105652173f4b617f",
"type": "github"
},
"original": {
"owner": "nix-community",
"repo": "fenix",
"type": "github"
}
},
"naersk": {
"inputs": {
"fenix": "fenix",
"nixpkgs": "nixpkgs"
},
"locked": {
"lastModified": 1752689277,
"narHash": "sha256-uldUBFkZe/E7qbvxa3mH1ItrWZyT6w1dBKJQF/3ZSsc=",
"owner": "nix-community",
"repo": "naersk",
"rev": "0e72363d0938b0208d6c646d10649164c43f4d64",
"type": "github"
},
"original": {
"owner": "nix-community",
"repo": "naersk",
"type": "github"
}
},
"nixpkgs": {
"locked": {
"lastModified": 1752077645,
"narHash": "sha256-HM791ZQtXV93xtCY+ZxG1REzhQenSQO020cu6rHtAPk=",
"owner": "NixOS",
"repo": "nixpkgs",
"rev": "be9e214982e20b8310878ac2baa063a961c1bdf6",
"type": "github"
},
"original": {
"owner": "NixOS",
"ref": "nixpkgs-unstable",
"repo": "nixpkgs",
"type": "github"
}
},
"nixpkgs_2": {
"locked": {
"lastModified": 1760934318,
"narHash": "sha256-/oUYsC0lUCBory65VK+UHqCCsCspbL1Vgfcf1KUYqVw=",
@@ -18,10 +75,28 @@
},
"root": {
"inputs": {
"nixpkgs": "nixpkgs",
"naersk": "naersk",
"nixpkgs": "nixpkgs_2",
"systems": "systems"
}
},
"rust-analyzer-src": {
"flake": false,
"locked": {
"lastModified": 1752428706,
"narHash": "sha256-EJcdxw3aXfP8Ex1Nm3s0awyH9egQvB2Gu+QEnJn2Sfg=",
"owner": "rust-lang",
"repo": "rust-analyzer",
"rev": "591e3b7624be97e4443ea7b5542c191311aa141d",
"type": "github"
},
"original": {
"owner": "rust-lang",
"ref": "nightly",
"repo": "rust-analyzer",
"type": "github"
}
},
"systems": {
"locked": {
"lastModified": 1681028828,

109
flake.nix
View File

@@ -12,29 +12,71 @@
nixpkgs,
systems,
naersk,
self,
...
}:
let
eachSystem = f: nixpkgs.lib.genAttrs (import systems) (system: f nixpkgs.legacyPackages.${system});
build_asciidoctor_revealjs = pkgs: pkgs.buildRubyGem {
gemName = "asciidoctor-revealjs";
version = "5.2.0";
source = {
type = "git";
url = "https://rubygems.org/downloads/asciidoctor-revealjs-5.2.0.gem";
sha256 = "sha256-NQSl5+ryyR3jf5YYkxT/sea/lNrZ1kbVyaJMZpG/auI=";
build_asciidoctor_revealjs =
pkgs:
pkgs.buildRubyGem {
gemName = "asciidoctor-revealjs";
version = "5.2.0";
source = {
type = "git";
url = "https://rubygems.org/downloads/asciidoctor-revealjs-5.2.0.gem";
sha256 = "sha256-NQSl5+ryyR3jf5YYkxT/sea/lNrZ1kbVyaJMZpG/auI=";
};
};
};
build_asciidoctor = pkgs: pkgs.buildRubyGem {
gemName = "asciidoctor";
version = "2.0.25";
source = {
type = "git";
url = "https://rubygems.org/downloads/asciidoctor-2.0.25.gem";
sha256 = "sha256-sG/oIrCRDU4l6g9WL+z8eUjpRuIi79lJSEnSfaQmfRk=";
build_asciidoctor =
pkgs:
pkgs.buildRubyGem {
gemName = "asciidoctor";
version = "2.0.25";
source = {
type = "git";
url = "https://rubygems.org/downloads/asciidoctor-2.0.25.gem";
sha256 = "sha256-sG/oIrCRDU4l6g9WL+z8eUjpRuIi79lJSEnSfaQmfRk=";
};
};
};
build_docki =
{ naerskLib, pkgs }:
let
runtimeDeps = with pkgs; [
(ruby.withPackages (
p: with p; [
(build_asciidoctor_revealjs pkgs)
(build_asciidoctor pkgs)
bundler
]
))
];
in
naerskLib.buildPackage {
src = ./.;
buildInputs = [
];
nativeBuildInputs = with pkgs; [
pkg-config
openssl.dev
libiconv
makeWrapper
];
postInstall = ''
mkdir -p $out/share/bash-completion/completions
mkdir -p $out/share/zsh/site-functions
mkdir -p $out/share/fish/vendor_completions.d
$out/bin/docki completions bash > $out/share/bash-completion/completions/docki
$out/bin/docki completions zsh > $out/share/zsh/site-functions/_docki
$out/bin/docki completions fish > $out/share/fish/vendor_completions.d/docki.fish
wrapProgram $out/bin/docki --prefix PATH : ${pkgs.lib.makeBinPath runtimeDeps}
'';
};
in
{
devShells = eachSystem (pkgs: {
@@ -56,36 +98,27 @@
))
];
};
preview = pkgs.mkShell {
name = "docki-preview";
buildInputs = [
pkgs.zsh
pkgs.fish
pkgs.bash
self.packages.${pkgs.system}.default
];
};
});
packages = eachSystem (
pkgs:
let
naerskLib = pkgs.callPackage naersk { };
runtimeDeps = with pkgs; [
(ruby.withPackages (
p: with p; [
(build_asciidoctor_revealjs pkgs)
(build_asciidoctor pkgs)
bundler
]
))
];
in
{
default = naerskLib.buildPackage {
src = ./.;
buildInputs =[
];
nativeBuildInputs = with pkgs; [
pkg-config
openssl.dev
libiconv
makeWrapper
];
postInstall = ''
wrapProgram $out/bin/docki --prefix PATH : ${pkgs.lib.makeBinPath runtimeDeps}
'';
default = build_docki {
naerskLib = naerskLib;
pkgs = pkgs;
};
}
);

View File

@@ -3,7 +3,14 @@ use clap::{Parser, Subcommand};
#[derive(Parser)]
pub struct Args {
#[command(subcommand)]
pub command: CommandArg
pub command: CommandArg,
}
#[derive(Subcommand)]
pub enum ShellArg {
Bash,
Fish,
Zsh,
}
#[derive(Subcommand)]
@@ -16,10 +23,13 @@ pub enum CommandArg {
InstallReveal,
/// Starts a Webserver with the live preview of the Documentation
Serve {
/// Port for the Live Server
/// Port for the Live Server
#[arg(short, long)]
port: Option<u16>
port: Option<u16>,
},
/// Generates completions for the desired shell
Completions {
#[command(subcommand)]
shell: ShellArg,
},
/// Information about the completions
Completions
}

View File

@@ -1,7 +1,5 @@
use std::process;
use regex::Regex;
fn exec_command(command: &mut process::Command) -> Result<(), String> {
let result = command.output();
@@ -31,7 +29,6 @@ fn asciidoctor_docs(in_path: &str, out_path: &str) -> process::Command {
fn asciidoctor_slides(in_path: &str, out_path: &str) -> process::Command {
let mut command = process::Command::new(format!("asciidoctor-revealjs"));
let out_dir = parent_path(out_path);
let revealjs_path = "/slides/revealjs";
command
@@ -43,60 +40,6 @@ fn asciidoctor_slides(in_path: &str, out_path: &str) -> process::Command {
return command;
}
fn parent_path(child_path: &str) -> String {
let split: Vec<&str> = child_path.split("/").collect();
let slice = &split[..split.len() - 1];
return slice.join("/");
}
pub fn path_between(from: String, to: String) -> String {
let from_segments = transform_input_to_clone_split(&from);
let to_segments = transform_input_to_clone_split(&to);
let last_matching_index = matching_from_start(&from_segments, &to_segments);
let number_of_backs = from_segments.len() - last_matching_index;
let mut path_between = path_back(number_of_backs);
let path_to_to_path = &to_segments[last_matching_index..];
path_between.push_str(&path_to_to_path.join("/"));
return path_between;
}
fn transform_input_to_clone_split(input: &String) -> Vec<String> {
let regex = Regex::new(r"/$").unwrap();
let first_transformation = input.clone().replace("./", "");
return regex
.replace_all(&first_transformation, "")
.to_string()
.split("/")
.collect::<Vec<&str>>()
.iter()
.map(|s| s.to_string())
.collect();
}
fn path_back(count: usize) -> String {
let mut path = "".to_string();
for _ in 0..count {
path.push_str("../");
}
return path;
}
pub fn matching_from_start(from_segments: &Vec<String>, to_segments: &Vec<String>) -> usize {
for (index, from_segment) in from_segments.iter().enumerate() {
if let Some(to_segment) = to_segments.get(index) {
if from_segment != to_segment {
return index;
}
} else {
return index;
}
}
return from_segments.len();
}
pub fn build_doc(in_path: &str, out_path: &str) -> Result<(), String> {
let mut command = asciidoctor_docs(in_path, out_path);
return exec_command(&mut command);

View File

@@ -6,10 +6,6 @@ use super::fs_util;
pub mod asciidoctor;
pub trait Builder {
fn build(&self, in_path: &str, out_path: &str) -> Result<(), String>;
}
pub fn docki_build(in_path: &str) -> DockiBuildResult {
let out_path = in_path.replace("/docs/", "/dist/");
let convert_out_path = out_path.replace(".adoc", ".html");
@@ -35,11 +31,11 @@ pub fn docki_build(in_path: &str) -> DockiBuildResult {
}
}
fn copy(in_path: &str, out_path: &str) -> Result<(), String> {
fn copy(in_path: &str, out_path: &str) -> Result<(), String> {
fs_util::create_parent_dir_recursive(out_path);
if let Err(err) = fs::copy(in_path, out_path) {
return Err(err.to_string())
return Err(err.to_string());
}
Ok(())

View File

@@ -1,21 +1,16 @@
use colored::Colorize;
use std::io;
const INFO: &str = "
You can add completions for docki with the following methods. If you want the completions to be persistent add
them to a init file e.g. ~/.zshrc, ~/.bashrc, ~/.config/fish/config.fish.
use clap::CommandFactory;
use clap_complete::{generate, shells::{Bash, Fish, Zsh}};
Get Fish Completions
source ~/.docki/completions/docki.fish
use crate::app::args::structure::{Args, ShellArg};
Get Zsh Completions
source ~/.docki/completions/_docki
pub fn completions(shell: ShellArg) {
let mut command = Args::command();
Get Bash Completions
source ~/.docki/completions/docki.bash
";
pub fn completions() {
println!();
println!("{}", "Completions".blue().bold());
println!("{}", INFO.bright_black());
match shell {
ShellArg::Bash => generate(Bash, &mut command, "docki", &mut io::stdout()),
ShellArg::Fish => generate(Fish, &mut command, "docki", &mut io::stdout()),
ShellArg::Zsh => generate(Zsh, &mut command, "docki", &mut io::stdout()),
}
}

View File

@@ -27,7 +27,7 @@ impl App {
CommandArg::Health => health(),
CommandArg::InstallReveal => install_reveal().await,
CommandArg::Serve { port } => serve(port).await,
CommandArg::Completions => completions()
CommandArg::Completions { shell } => completions(shell)
};
}

View File

@@ -1,37 +0,0 @@
use crate::app::build::asciidoctor;
#[test]
fn last_matching_index_0() {
let vec1 = vec!["dings", "dings", "dingens"].iter().map(|s| s.to_string()).collect();
let vec2 = vec!["dings", "dings", "dings"].iter().map(|s| s.to_string()).collect();
let last_maching = asciidoctor::matching_from_start(&vec1, &vec2);
assert_eq!(last_maching, 2);
}
#[test]
fn last_matching_index_1() {
let vec1 = vec!["dings", "dings", "dingens", "dings", "dingens"].iter().map(|s| s.to_string()).collect();
let vec2 = vec!["dings", "dings", "dingens", "dings"].iter().map(|s| s.to_string()).collect();
let last_maching = asciidoctor::matching_from_start(&vec1, &vec2);
assert_eq!(last_maching, 4);
}
#[test]
fn path_between_0() {
let path1 = "./docs/dings";
let path2 = "./dist/dings";
let path_between = asciidoctor::path_between(path1.to_string(), path2.to_string());
assert_eq!(path_between, "../../dist/dings");
}
#[test]
fn path_between_1() {
let path1 = "./dist/slides/core/";
let path2 = "./dist/slides/revealjs";
let path_between = asciidoctor::path_between(path1.to_string(), path2.to_string());
assert_eq!(path_between, "../revealjs")
}