Skip to content

Commit

Permalink
refactor: rewrite basics according to new spec
Browse files Browse the repository at this point in the history
  • Loading branch information
norskeld committed Jan 6, 2024
1 parent 870d281 commit 1f87faf
Show file tree
Hide file tree
Showing 21 changed files with 1,418 additions and 772 deletions.
9 changes: 7 additions & 2 deletions Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -8,16 +8,21 @@ repository = "https://github.com/norskeld/arx"
publish = false

[dependencies]
chumsky = { version = "0.8.0" }
anyhow = { version = "1.0.76" }
clap = { version = "4.4.11", features = ["cargo", "derive"] }
console = { version = "0.15.7" }
dirs = "5.0.1"
flate2 = { version = "1.0.28" }
git2 = { version = "0.18.1", features = ["vendored-libgit2"] }
glob-match = { version = "0.2.1" }
indoc = "2.0.4"
itertools = { version = "0.12.0" }
kdl = { version = "4.6.0" }
petgraph = { version = "0.6.4", features = ["stable_graph"] }
reqwest = { version = "0.11.22", features = ["json"] }
run_script = { version = "0.10.1" }
shellexpand = { version = "3.1.0", features = ["full"] }
tar = { version = "0.4.40" }
thiserror = { version = "1.0.51" }
tokio = { version = "1.35.0", features = ["macros", "fs", "rt-multi-thread"] }
walkdir = { version = "2.4.0" }

Expand Down
18 changes: 0 additions & 18 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -10,24 +10,6 @@ Simple CLI for scaffolding projects from templates in a touch.

WIP.

## Features

`arx` allows you to make copies of git repositories, much like [degit], but with added sugar on top of its basic functionality to help scaffold projects even faster and easier.

Some of that sugar includes:

- Ability to define [replacement tags](#replacements) (aka placeholders) and simple [actions](#actions) to perform on the repository being copied. This is done via `arx.kdl` config file using the [KDL document language][kdl], which is really easy to grasp, write and read, unlike ubiquitous **JSON** and **YAML**.

- Automatically generated prompts based on the `arx.kdl` config, that will allow you to interactively replace placeholders with actual values and (optionally) run only selected actions.

## Replacements

> TODO: Document replacements.
## Actions

> TODO: Document actions.
## Acknowledgements

Thanks to [Rich Harris][rich-harris] and his [degit] tool for inspiration. `:^)`
Expand Down
140 changes: 73 additions & 67 deletions src/app.rs
Original file line number Diff line number Diff line change
@@ -1,99 +1,105 @@
use std::fmt;
use std::fs;
use std::path::PathBuf;
use std::str::FromStr;

use clap::Parser;

use crate::config::{self, Action};
use crate::manifest::Manifest;
use crate::path::PathUtils;
use crate::repository::{Repository, RepositoryMeta};
use crate::tar;
use crate::{parser, processing};

/// Newtype for app errors which get propagated across the app.
#[derive(Debug)]
pub struct AppError(pub String);

impl fmt::Display for AppError {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
write!(f, "{message}", message = self.0)
}
}
use crate::unpacker::Unpacker;

#[derive(Parser, Debug)]
#[clap(version, about, long_about = None)]
pub struct App {
pub struct Cli {
/// Repository to download.
#[clap(name = "target")]
target: String,
pub target: String,

/// Directory to download to.
#[clap(name = "path")]
path: Option<String>,

/// Init git repository.
#[clap(short, long, display_order = 0)]
git: bool,
pub path: Option<String>,

/// Remove imp config after download.
/// Delete arx config after download.
#[clap(short, long, display_order = 1)]
remove: bool,

/// Do not run actions defined in the repository.
#[clap(short, long, display_order = 2)]
ignore: bool,
pub delete: bool,

/// Download at specific ref (branch, tag, commit).
/// Download using specified ref (branch, tag, commit).
#[clap(short, long, display_order = 3)]
meta: Option<String>,
pub meta: Option<String>,
}

pub async fn run() -> Result<(), AppError> {
let options = App::parse();
pub struct App;

// Parse repository information from the CLI argument.
let repository = parser::shortcut(&options.target)?;

// Now check if any specific meta (ref) was passed, if so, then use it; otherwise use parsed meta.
let meta = options.meta.map_or(repository.meta, RepositoryMeta);
let repository = Repository { meta, ..repository };
impl App {
pub fn new() -> Self {

Check failure on line 35 in src/app.rs

View workflow job for this annotation

GitHub Actions / cargo clippy

you should consider adding a `Default` implementation for `App`
Self
}

// Fetch the tarball as bytes (compressed).
let tarball = repository.fetch().await?;
pub async fn run(&mut self) -> anyhow::Result<()> {
// Parse CLI options.
let options = Cli::parse();

// Get destination path.
let destination = options
.path
.map(PathBuf::from)
.unwrap_or_else(|| PathBuf::from(repository.repo));
// Parse repository information from the CLI argument.
let repository = Repository::from_str(&options.target)?;

// Decompress and unpack the tarball.
let unpacked = tar::unpack(&tarball, &destination)?;
// Check if any specific meta (ref) was passed, if so, then use it; otherwise use parsed meta.
let meta = options.meta.map_or(repository.meta(), RepositoryMeta);
let repository = repository.with_meta(meta);

// Read the kdl config.
let arx_config = config::resolve_arx_config(&destination)?;
// TODO: Check if destination already exists before downloading or performing local clone.

// Get replacements and actions.
let replacements = config::get_replacements(&arx_config);
let actions = config::get_actions(&arx_config);
// Depending on the repository type, either download and unpack or make a local clone.
let destination = match repository {
| Repository::Remote(remote) => {
let name = options.path.unwrap_or(remote.repo.clone());
let destination = PathBuf::from(name);

if let Some(items) = replacements {
processing::process_replacements(&unpacked, &items).await?;
}
// Fetch the tarball as bytes (compressed).
let tarball = remote.fetch().await?;

if let Some(action) = actions {
match action {
| Action::Suite(suites) => {
let (resolved, unresolved) = config::resolve_requirements(&suites);
// Decompress and unpack the tarball.
let unpacker = Unpacker::new(tarball);
unpacker.unpack_to(&destination)?;

println!("-- Action suites:");
println!("Resolved: {resolved:?}");
println!("Unresolved: {unresolved:?}");
destination
},
| Action::Single(actions) => {
println!("-- Actions:");
println!("Resolved: {actions:?}");
| Repository::Local(local) => {
// TODO: Check if source exists and valid.
let source = PathBuf::from(local.source.clone()).expand();

Check failure on line 69 in src/app.rs

View workflow job for this annotation

GitHub Actions / cargo clippy

useless conversion to the same type: `std::path::PathBuf`

let destination = if let Some(destination) = options.path {
PathBuf::from(destination).expand()
} else {
source
.file_name()
.map(|name| name.into())
.unwrap_or_default()
};

// Copy the directory.
local.copy(&destination)?;
local.checkout(&destination)?;

// Delete inner .git.
let inner_git = destination.join(".git");

if inner_git.exists() {
println!("Removing {}", inner_git.display());
fs::remove_dir_all(inner_git)?;
}

// TODO: Check if source is a plain directory or git repo. If the latter, then we should
// also do a checkout.

destination
},
}
}
};

// Now we need to read the manifest (if it is present).
let mut manifest = Manifest::with_options(&destination);
manifest.load()?;

Ok(())
Ok(())
}
}
Loading

0 comments on commit 1f87faf

Please sign in to comment.