summaryrefslogtreecommitdiffstats
path: root/xtask
diff options
context:
space:
mode:
authorToby Vincent <tobyv13@gmail.com>2023-06-01 16:22:10 -0500
committerToby Vincent <tobyv13@gmail.com>2023-06-01 19:03:25 -0500
commit7201e443e19b45a2feb24ef470c6380b0859e52f (patch)
tree6c5fc66926db06faa29101cb8f57793ed9e9ba2c /xtask
parenteaf5c71873705b9593ec0e6b34d7e529d74a9269 (diff)
build: improve build tooling and add CD
Implement a xtask pattern tool for packaging into distributable. Add build script to generate completion scripts and man page. Improve PKGBUILDs and CI to use new tooling and upload artifact to tag after successful CI build. Make minor changes in cli config to facilitate the new build.rs script.
Diffstat (limited to 'xtask')
-rw-r--r--xtask/Cargo.toml15
-rw-r--r--xtask/src/lib.rs102
-rw-r--r--xtask/src/main.rs65
3 files changed, 182 insertions, 0 deletions
diff --git a/xtask/Cargo.toml b/xtask/Cargo.toml
new file mode 100644
index 0000000..5040f0a
--- /dev/null
+++ b/xtask/Cargo.toml
@@ -0,0 +1,15 @@
+[package]
+name = "xtask"
+version = { workspace = true }
+edition = { workspace = true }
+authors = { workspace = true }
+homepage = { workspace = true }
+repository = { workspace = true }
+license = { workspace = true }
+publish = false
+
+[dependencies]
+anyhow = { workspace = true }
+clap = { workspace = true }
+tar = "0.4.38"
+flate2 = "1.0.26"
diff --git a/xtask/src/lib.rs b/xtask/src/lib.rs
new file mode 100644
index 0000000..5e876d5
--- /dev/null
+++ b/xtask/src/lib.rs
@@ -0,0 +1,102 @@
+use std::{
+ env::consts::ARCH,
+ fs::File,
+ path::{Path, PathBuf},
+};
+
+use anyhow::{ensure, Result};
+use flate2::{write::GzEncoder, Compression};
+use tar::Builder;
+
+const BIN_NAME: &str = "projectr";
+const PKG_VER: &str = env!("CARGO_PKG_VERSION");
+const PKG_INCLUDE: &[&str] = &[
+ "bin/tmux-projectr",
+ "CONTRIBUTING.md",
+ "README.md",
+ "LICENSE",
+];
+
+pub fn generate_tar_gz<P: AsRef<Path>>(
+ root: &P,
+ profile: &str,
+ tag: Option<&str>,
+) -> Result<PathBuf> {
+ let pkg_name = format!("{BIN_NAME}-{PKG_VER}-{ARCH}.tar.gz");
+
+ let target_dir = root.as_ref().join("target");
+ let profile_dir = target_dir.join(profile);
+ let dist_dir = target_dir.join("dist");
+ let out_dir = find_out_dir(&profile_dir)?;
+
+ let bin_path = profile_dir.join(BIN_NAME);
+ let pkg_path = dist_dir.join(pkg_name);
+
+ ensure!(
+ !tag.is_some_and(|t| t.trim_start_matches('v') != PKG_VER),
+ "Package version does not match provided tag: {PKG_VER} != {}",
+ tag.unwrap().trim_start_matches('v')
+ );
+
+ ensure!(
+ bin_path.exists(),
+ "Package binary does not exist: {}",
+ bin_path.display()
+ );
+
+ ensure!(
+ out_dir.exists(),
+ "Build's out directory does not exist: {}",
+ out_dir.display()
+ );
+
+ let _ = std::fs::remove_dir_all(&dist_dir);
+ std::fs::create_dir_all(&dist_dir)?;
+
+ let tar_gz = File::create(&pkg_path)?;
+ let enc = GzEncoder::new(tar_gz, Compression::default());
+ let mut tar = Builder::new(enc);
+
+ std::env::set_current_dir(root)?;
+
+ tar.append_path_with_name(bin_path, PathBuf::from("bin").join(BIN_NAME))?;
+ tar.append_dir_all(".", out_dir)?;
+ PKG_INCLUDE.iter().try_for_each(|p| tar.append_path(p))?;
+
+ tar.into_inner()?.finish()?;
+
+ Ok(pkg_path)
+}
+
+pub fn find_out_dir<P: AsRef<Path>>(profile_dir: P) -> Result<PathBuf> {
+ let build_dir = profile_dir.as_ref().join("build");
+
+ build_dir
+ .read_dir()
+ .map_err(|_| {
+ anyhow::anyhow!(
+ "Package build directory does not exist: {}",
+ build_dir.display()
+ )
+ })?
+ .flatten()
+ .filter_map(|d| {
+ d.file_name()
+ .to_str()?
+ .starts_with(BIN_NAME)
+ .then(|| d.path().join("invoked.timestamp"))
+ .filter(|p| p.exists())
+ })
+ .reduce(|acc, path_buf| {
+ std::cmp::max_by_key(path_buf, acc, |p| {
+ p.metadata()
+ .and_then(|m| m.modified())
+ .unwrap_or(std::time::SystemTime::UNIX_EPOCH)
+ })
+ })
+ .map(|p| p.with_file_name("out"))
+ .ok_or(anyhow::anyhow!(
+ "Package out directory not found: {}",
+ profile_dir.as_ref().display()
+ ))
+}
diff --git a/xtask/src/main.rs b/xtask/src/main.rs
new file mode 100644
index 0000000..2282efd
--- /dev/null
+++ b/xtask/src/main.rs
@@ -0,0 +1,65 @@
+//! See <https://github.com/matklad/cargo-xtask/>.
+//!
+//! This binary defines various auxiliary build commands, which are not
+//! expressible with just `cargo`.
+//!
+//! This binary is integrated into the `cargo` command line by using an alias in
+//! `.cargo/config`.
+
+use std::path::PathBuf;
+
+use anyhow::Result;
+use clap::{Parser, Subcommand};
+
+fn main() -> Result<()> {
+ let cli = Cli::parse();
+
+ match cli.command {
+ Commands::Dist { tag } => {
+ let targz = xtask::generate_tar_gz(&cli.directory, &cli.profile, tag.as_deref())?;
+ println!("{}", targz.display());
+ }
+ Commands::OutDir => {
+ let profile_dir = cli.directory.join("target").join(&cli.profile);
+ let out_dir = xtask::find_out_dir(profile_dir)?;
+ println!("{}", out_dir.display());
+ }
+ };
+
+ Ok(())
+}
+
+#[derive(Debug, Clone, Parser)]
+#[command(author, version, about)]
+struct Cli {
+ #[command(subcommand)]
+ command: Commands,
+
+ /// Cargo profile to use.
+ #[arg(short, long, default_value = "release")]
+ profile: String,
+
+ /// Path to root directory of package.
+ #[arg(short='C', long, default_value_os_t = get_package_dir())]
+ directory: PathBuf,
+}
+
+#[derive(Debug, Clone, Subcommand)]
+enum Commands {
+ /// Print the default value of OUT_DIR used by cargo when building the package.
+ OutDir,
+
+ /// Generate distributable package and print it's path
+ Dist {
+ /// Verify the package version matches the provided tag.
+ #[arg(short, long)]
+ tag: Option<String>,
+ },
+}
+
+fn get_package_dir() -> PathBuf {
+ std::path::Path::new(&env!("CARGO_MANIFEST_DIR"))
+ .parent()
+ .unwrap()
+ .to_path_buf()
+}