From 3ba7200c9ddad3092681e3fdda1ae0dc91887e7e Mon Sep 17 00:00:00 2001 From: roylaurie Date: Thu, 23 May 2024 10:10:55 -0700 Subject: [PATCH] v0.3.1 - Now working on Windows (x64) and macOS (M3) - Diff ouput is cleaner - Packaging supports remote building of native Windows and macOS --- .gitignore | 1 + Building.md | 27 ++++++++++---- Cargo.toml | 6 +--- README.md | 4 +-- pkg.cfg.template | 2 ++ snapcraft.yaml | 30 +++++++++++----- src/cli.rs | 8 ++--- src/lib.rs | 64 ++++++++++++++++++++++++++++----- src/os.rs | 73 +++++++++++++++++++++++--------------- tests/common/mod.rs | 2 ++ tests/exe.rs | 12 ++++--- tests/standard.rs | 14 +++----- tools/build-release.bash | 53 +++++++++++++++++++++++++-- tools/common.lib.bash | 25 +++++++++++-- tools/package-all.bash | 4 +-- tools/package-debian.bash | 7 ++-- tools/package-rpm.bash | 6 +--- tools/package-tarball.bash | 17 ++++----- tools/release.bash | 19 ++++++++++ 19 files changed, 273 insertions(+), 101 deletions(-) create mode 100644 pkg.cfg.template diff --git a/.gitignore b/.gitignore index 4c14424..437ef4e 100644 --- a/.gitignore +++ b/.gitignore @@ -1,5 +1,6 @@ /target /Cargo.lock +/pkg.cfg /.vscode *.snap *.bak diff --git a/Building.md b/Building.md index 3494279..19af212 100644 --- a/Building.md +++ b/Building.md @@ -1,18 +1,31 @@ # Building bak9 -Most of these steps are for cross-compilation. +These steps are designed for cross-compilation from an Ubuntu 24 linux distro, +using a Windows 11 Pro VM and hardware running macOS Sonoma on an M3 CPU. +## Rust Targets -## Requirements +To install a target: +```bash +rustup target add $TARGET +rustup toolchain install stable-$TARGET +``` -*Tested against Ubuntu Desktop 23* +- x86_64-unknown-linux-gnu +- armv7-unknown-linux-gnueabihf +- aarch64-unknown-linux-gnu +- aarch64-apple-darwin +- x86_64-pc-windows-msvc + +## Requirements Debian packaging requires: ```bash sudo apt install \ -pkg-config build-essential \ -cross-build-essential-arm64 \ -cross-build-essential-armhf +pkg-config \ +build-essential \ +crossbuild-essential-arm64 \ +crossbuild-essential-armhf ``` Snap packaging requires: @@ -27,7 +40,7 @@ cargo install cross Debian packaging requires: ```bash -cargo install carg-deb +cargo install cargo-deb ``` RPM packaging requires: diff --git a/Cargo.toml b/Cargo.toml index 1d7d55f..ee1af4e 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -1,6 +1,6 @@ [package] name = "bak9" -version = "0.3.0" +version = "0.3.1" edition = "2021" description = "Creates a backup .bak copy of a file" authors = ["Asmov LLC "] @@ -31,7 +31,3 @@ file_diff = "1" assets = [ { source = "target/release/bak", dest = "/usr/bin/bak", mode = "755" } ] - -[package.metadata.packager] - - diff --git a/README.md b/README.md index 500530b..bed2616 100644 --- a/README.md +++ b/README.md @@ -22,7 +22,7 @@ user's app data directory will be used instead. If *multiple* backups of FILE exist, the rotating filename extension used will be: `.bak.N`. -The most recent rotating backup will be always `.bak.0`. +The most recent rotating backup will always be `.bak.0`. Pruning of rotating backups occurs after `-n NUM` backups. @@ -47,7 +47,7 @@ Force the operation without confirmation. Lists all backups of FILE in DIR. - `diff N` -Shows the differences of FILE and its `bak.N` copy in DIR. [default: 0] +Shows the differences between FILE and the specified `bak.N` backup in DIR. [default: 0] - `rm` Deletes all backups of FILE in DIR. diff --git a/pkg.cfg.template b/pkg.cfg.template new file mode 100644 index 0000000..3baa1d6 --- /dev/null +++ b/pkg.cfg.template @@ -0,0 +1,2 @@ +WINDOWS_REMOTE="" +MACOS_REMOTE="" diff --git a/snapcraft.yaml b/snapcraft.yaml index 21ba541..b2e0468 100644 --- a/snapcraft.yaml +++ b/snapcraft.yaml @@ -2,25 +2,37 @@ name: bak9 version: git summary: Creates a backup bak copy of a file description: | - Creates a backup `.bak` copy of a file. + bak [OPTIONS] FILE [DIR] [COMMAND] - Usage: bak [OPTION]... FILE [DIR] + Creates a backup .bak copy of FILE. If DIR is not specified, the copy is created in the same directory as FILE. - If multiple backups of FILE exist, the filename extension used will be `.bak.N`. + If DIR is specifed as -, or if the user lacks permissions to copy to DIR, the user's app data directory will be used instead. - Pruning occurs after `-n NUM` backups. + If multiple backups of FILE exist, the rotating filename extension used will be: .bak.N. -base: core22 + The most recent rotating backup will always be .bak.0. + + Pruning of rotating backups occurs after -n NUM backups. + + If the current backup is no different than its predecessor, copying will be skipped. + + Additional COMMANDs may be appended to list, compare, or delete backups. + + +base: core24 confinement: classic -architectures: - - build-on: amd64 +platforms: + amd64: + build-on: amd64 build-for: amd64 - - build-on: [amd64, arm64] + arm64: + build-on: [amd64, arm64] build-for: [arm64] - - build-on: [amd64, armhf] + armhf: + build-on: [amd64, armhf] build-for: [armhf] parts: diff --git a/src/cli.rs b/src/cli.rs index ef5e173..e3209e9 100644 --- a/src/cli.rs +++ b/src/cli.rs @@ -3,7 +3,7 @@ use clap::{Parser, Subcommand}; use crate::{PathExt, E_STR}; -#[derive(Parser)] +#[derive(Parser, Debug)] #[command(version, about)] pub struct Cli { #[command(subcommand)] @@ -27,15 +27,15 @@ pub struct Cli { } -#[derive(Subcommand)] +#[derive(Subcommand, Debug)] pub enum Command { #[command(name = "ls", about = "List all backups of FILE in DIR")] List, #[command(name = "rm", about = "Deletes all backups of FILE in DIR")] Wipe, - #[command(name = "diff", about = "Shows the differences between FILE and BAK.N")] + #[command(name = "diff", about = "Shows the differences between FILE and bak.N in DIR")] Diff { - #[arg(default_value_t = 0, help = "The BAK index to compare FILE with")] + #[arg(default_value_t = 0, help = "The .bak.N index to compare FILE with")] index: u8, } } diff --git a/src/lib.rs b/src/lib.rs index 4c9d7aa..d8a793b 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -194,7 +194,7 @@ fn confirm_wipe(source_file: &Path, dir: &Path, force: bool) -> bool { print!("{} Delete all backups of {} in {}? {} ", "confirm:".bright_yellow(), source_file.filename_str().expect(E_FILENAME).cyan(), - dir.to_str().expect(E_STR).cyan(), + sanitize_path_str(dir.to_str().expect(E_STR)).cyan(), "(y/N):".magenta()); std::io::stdout().flush() @@ -283,7 +283,9 @@ fn print_list_backups(source_file: &Path, dir: &Path) -> Result<(), Error> { return Ok(()) } - println!("Backups of {file} in {dir}:", file = source_file.to_str().expect(E_STR).cyan(), dir = dir.to_str().expect(E_STR).cyan()); + println!("Backups of {file} in {dir}:", + file = sanitize_path_str(source_file.to_str().expect(E_STR)).cyan(), + dir = sanitize_path_str(dir.to_str().expect(E_STR)).cyan()); for bak_filepath in bak_filepaths { println!(" {}", bak_filepath.filename_str().expect(E_STR).green()); @@ -344,7 +346,7 @@ fn wipe(source_file: &Path, dest_dir: &Path) -> Result<(), Error> { } /// Retrieves a list of all `.bak.N` files in the directory. -fn list_bak_n_files(file: &Path, dir: &Path, ) -> Result, Error> { +fn list_bak_n_files(file: &Path, dir: &Path) -> Result, Error> { let bak_n_file_pattern = file .append_extension(BAK_DOT) .filename_string().expect(E_FILENAME); @@ -411,7 +413,8 @@ fn run_backup(cli: &cli::Cli) -> Result<(), Error> { .map_err(|_| Error::copy(&cli.file, &home_bak_filepath, e))?; if !cli.quiet { - eprintln!("{} copied to {}", "notice:".yellow(), home_bak_filepath.to_str().expect("Expected string").cyan()); + eprintln!("{} copied to {}", "notice:".yellow(), + sanitize_path_str(home_bak_filepath.to_str().expect(E_STR)).cyan()); } Ok(()) @@ -466,21 +469,42 @@ fn determine_destination(source_file: &Path, dest_dir: &Path, max: u8) -> Result Ok(Some(bak_filepath)) } -pub fn mirror_dir(base_dir: &Path, src_file: &Path, mkdir: bool) -> Result { +pub fn sanitize_path_str(path: &str) -> &str { + sanitize_windows_path_str(path) +} + +pub fn sanitize_windows_path_str(path: &str) -> &str { + path.trim_start_matches("\\\\?\\") +} + +fn determine_mirror_dir(base_dir: &Path, src_file: &Path) -> Result { let src_dir = src_file.parent().expect("Expected parent directory"); let mut mirror_dir = base_dir.to_path_buf(); for component in src_dir.components() { - let dirname = component.as_os_str().to_str().expect(E_STR); + let dirname = component.as_os_str().to_str().expect(E_STR) + .trim_start_matches("\\\\?\\"); // remove any windows extended path prefix + match dirname { - "." | "/" => continue, + "." | "/" | "\\" => continue, ".." => unreachable!("Expected absolute path"), _ => {} } - mirror_dir.push(dirname); + // windows drives (C:, D:, etc) + if dirname.chars().count() == 2 && dirname.chars().nth(1).unwrap() == ':' { + mirror_dir.push(dirname.chars().nth(0).unwrap().to_string()); + } else { + mirror_dir.push(dirname); + } } + Ok(mirror_dir) +} + +pub fn mirror_dir(base_dir: &Path, src_file: &Path, mkdir: bool) -> Result { + let mirror_dir = determine_mirror_dir(base_dir, src_file)?; + if !mirror_dir.is_dir() && mkdir { fs::create_dir_all(&mirror_dir) .map_err(|e| Error::io(IoOp::Create, &mirror_dir, e))? @@ -551,3 +575,27 @@ fn find_last_bak(file: &Path, dir: &Path) -> Option { } } } + +#[cfg(test)] +mod tests { + use std::path::Path; + use super::*; + + #[test] + fn test_determine_mirror_dir() { + let base_dir = "/home/dev/.local/share/bak9"; + let src_file = "/home/dev/tmp/source.txt"; + let mirror_dir = determine_mirror_dir(Path::new(base_dir), Path::new(src_file)).unwrap(); + assert_eq!(Path::new("/home/dev/.local/share/bak9/home/dev/tmp"), mirror_dir); + } + + #[cfg(target_os = "windows")] + #[test] + fn test_determine_mirror_dir_windows() { + // test a windows path with path extensions + let base_dir = "\\\\?\\C:\\Users\\dev\\AppData\\Local\\bak9"; + let src_file = "\\\\?\\C:\\Users\\dev\\tmp\\source.txt"; + let mirror_dir = determine_mirror_dir(Path::new(base_dir), Path::new(src_file)).unwrap(); + assert_eq!("\\\\?\\C:\\Users\\dev\\AppData\\Local\\bak9\\C\\Users\\dev\\tmp", mirror_dir.to_str().unwrap()); + } +} \ No newline at end of file diff --git a/src/os.rs b/src/os.rs index 8333acb..c2b0cf5 100644 --- a/src/os.rs +++ b/src/os.rs @@ -2,6 +2,15 @@ use std::{env, fs, io, path::{Path, PathBuf}}; use crate::E_STR; +fn sanitize_cmd_path(path: &Path) -> &str { + let path = path.to_str().expect(E_STR); + if cfg!(target_os = "windows") { + path.trim_start_matches("\\\\?\\") + } else { + path + } +} + pub fn copy_file(source: &Path, dest: &Path) -> io::Result<()> { #[cfg(target_os = "linux")] match linux_cp(source, dest) { @@ -13,6 +22,7 @@ pub fn copy_file(source: &Path, dest: &Path) -> io::Result<()> { .map(|_| ()) } +#[cfg(target_os = "linux")] fn linux_cp(source: &Path, dest: &Path) -> io::Result<()> { let output = std::process::Command::new("cp") .arg("--preserve") @@ -39,13 +49,13 @@ pub fn print_diff(source: &Path, file_b: &Path) -> Result<(), crate::Error> { .arg("diff") .arg("--no-index") .arg("--color") - .arg(source) - .arg(file_b) + .arg(sanitize_cmd_path(file_b)) + .arg(sanitize_cmd_path(source)) .output(); match output { Ok(output) => { - let lines: String = String::from_utf8(output.stdout).expect(E_STR) + let lines: String = String::from_utf8(output.stdout).expect(E_STR).trim() .lines() .skip(2) .map(|line| format!("{line}\n")) @@ -57,34 +67,30 @@ pub fn print_diff(source: &Path, file_b: &Path) -> Result<(), crate::Error> { Err(_) => {} // try system 'diff' } - #[cfg(any(target_os = "linux", target_os = "macos"))] - { + if cfg!(any(target_os = "linux", target_os = "macos")) { let output = std::process::Command::new("diff") + .arg("--color=always") .arg("-c") - .arg(source) .arg(file_b) + .arg(source) .output() .map_err(|e| crate::Error::Generic(e.to_string()))?; - println!("{}", String::from_utf8(output.stdout).expect(E_STR)); + println!("{}", String::from_utf8(output.stdout).expect(E_STR).trim()); Ok(()) - } - - #[cfg(target_os = "windows")] - { + } else if cfg!(target_os = "windows") { let output = std::process::Command::new("powershell") - .arg(format!("compare-object")) - .arg(format("(get-content {})", source)) - .arg(format("(get-content {})", file_b)) + .arg("compare-object") + .arg(format!("(get-content {})", sanitize_cmd_path(file_b))) + .arg(format!("(get-content {})", sanitize_cmd_path(source))) .output() .map_err(|e| crate::Error::Generic(e.to_string()))?; - println!("{}", String::from_utf8(output.stdout).expect(E_STR)); + println!("{}", String::from_utf8(output.stdout).expect(E_STR).trim()); Ok(()) + } else { + Err(crate::Error::Generic("Unsupported OS".to_string())) } - - #[cfg(not(any(target_os = "linux", target_os = "windows", target_os = "macos")))] - Err(crate::Error::Generic("Unsupported OS".to_string())) } /// Retrieves the bak9 data directory if possible, otherwise None. @@ -92,9 +98,9 @@ pub fn user_app_data_dir(mkdir: bool, app_subdirs: PathBuf) -> io::Result io::Result { fs::create_dir_all(&dir)?; dir.canonicalize() } else { - Err(io::Error::new(io::ErrorKind::NotFound, format!("Linux user app data directory not found: {}", dir.to_str().unwrap()))) + Err(io::Error::new(io::ErrorKind::NotFound, + format!("Linux user app data directory not found: {}", dir.to_str().unwrap()))) } } @@ -138,10 +145,14 @@ const ENV_LOCAL_APP_DATA: &str = "LocalAppData"; #[cfg(target_os = "windows")] fn windows_user_app_data_dir() -> io::Result { - let dir = env::var(ENV_LOCAL_APP_DATA)?.into(); + let dir: PathBuf = env::var(ENV_LOCAL_APP_DATA) + .map_err(|_| io::Error::new(io::ErrorKind::NotFound, + format!("Windows %LocalAppData% not found")))? + .into(); dir.canonicalize() - .map_err(|e| io::Error::new(io::ErrorKind::NotFound, "Windows user app data directory not found: {}", dir.to_str().unwrap())) + .map_err(|_| io::Error::new(io::ErrorKind::NotFound, + format!("Windows user app data directory not found: {}", dir.to_str().unwrap()))) } #[cfg(target_os = "macos")] @@ -151,11 +162,15 @@ const HOME: &str = "HOME"; #[cfg(target_os = "macos")] fn macos_user_app_data_dir() -> io::Result { - let dir = env::var(HOME)? - .into() - .join(MACOS_LIBRARY_APP_SUPPORT); - - dir.canonicalize() - .map_err(|e| io::Error::new(io::ErrorKind::NotFound, "MacOS user app data directory not found: {}", dir.to_str().unwrap())) + let dir: PathBuf = env::var(HOME) + .map_err(|_| io::Error::new(io::ErrorKind::NotFound, + format!("macOS HOME is not set")))? + .into(); + + dir + .join(MACOS_LIBRARY_APP_SUPPORT) + .canonicalize() + .map_err(|_| io::Error::new(io::ErrorKind::NotFound, + format!("macOS user app data directory not found: {}", dir.to_str().unwrap()))) } diff --git a/tests/common/mod.rs b/tests/common/mod.rs index 1eb9f01..11800ec 100644 --- a/tests/common/mod.rs +++ b/tests/common/mod.rs @@ -1,3 +1,5 @@ +#![allow(dead_code)] + use std::path::PathBuf; use file_diff; diff --git a/tests/exe.rs b/tests/exe.rs index 8e483ef..494ef9e 100644 --- a/tests/exe.rs +++ b/tests/exe.rs @@ -144,7 +144,7 @@ pub mod tests { let (stdout, stderr) = cmd(true, &[source_filepath.to_str().unwrap(), "ls"]); assert!(stderr.is_empty(), "stderr: {}", stderr); let lines: Vec<&str> = stdout.lines().collect(); - assert_eq!(lines.len(), 4); // line 0 is a header + assert_eq!(4, lines.len(), "stdout: {}", lines.into_iter().collect::()); // line 0 is a header assert_eq!(lines[1].trim(), SOURCE_TXT_BAK_0); assert_eq!(lines[2].trim(), SOURCE_TXT_BAK_1); assert_eq!(lines[3].trim(), SOURCE_TXT_BAK_2); @@ -155,7 +155,7 @@ pub mod tests { let (stdout, stderr) = cmd(true, &[source_filepath.to_str().unwrap(), "ls"]); assert!(stderr.is_empty(), "stderr: {}", stderr); let lines: Vec<&str> = stdout.lines().collect(); - assert_eq!(lines.len(), 6); // line 0 and 4 are headers + assert_eq!(6, lines.len()); // line 0 and 4 are headers assert_eq!(lines[1].trim(), SOURCE_TXT_BAK_0); assert_eq!(lines[2].trim(), SOURCE_TXT_BAK_1); assert_eq!(lines[3].trim(), SOURCE_TXT_BAK_2); @@ -195,9 +195,13 @@ pub mod tests { let (stdout, stderr) = cmd(true, &[source_filepath.to_str().unwrap(), "diff", "1"]); assert!(stderr.is_empty(), "stderr: {}", stderr); // the last line should be either "< TESTING_CONTENT" (diff) or "-TESTING_CONTENT" (git) - let last_line = stdout.lines().collect::>().last().unwrap().trim(); + let last_line = *stdout.trim().lines().collect::>().last().unwrap(); // use .contains() to (poorly) ignore terminal color codes - assert!(last_line.contains(&format!("> {}", TESTING_CONTENT)) || last_line.contains(&format!("-{}", TESTING_CONTENT))); + assert!( + last_line == &format!("\u{1b}[32m+\u{1b}[m\u{1b}[32m{}\u{1b}[m", TESTING_CONTENT) // git diff + || last_line == &format!("\u{1b}[32m+ {}\u{1b}[0m", TESTING_CONTENT) // gnu diff + || last_line == &format!("{} =>", TESTING_CONTENT), // windows diff + ); close_tmpdir(function_name!()); } diff --git a/tests/standard.rs b/tests/standard.rs index f9bee39..71d310a 100644 --- a/tests/standard.rs +++ b/tests/standard.rs @@ -4,6 +4,7 @@ mod common; mod tests { use super::common::*; use bak9; + use clap::Parser; use std::path::PathBuf; #[test] @@ -207,15 +208,10 @@ mod tests { #[test] fn test_app_data_dir_mirror() { let tmpdir = open_tmpdir(function_name!()); - std::fs::write(tmpdir.join("source.txt"), "LINE 1").unwrap(); - bak9::run_with(bak9::cli::Cli { - file: tmpdir.join("source.txt"), - dir: Some(PathBuf::from("-")), - num: 3, - force: true, - quiet: true, - subcommand: None, - }).unwrap(); + let source_filepath = tmpfile_append("LINE 1", "source.txt", function_name!()); + bak9::run_with( + bak9::cli::Cli::parse_from(["-f", "-q", "-n", "3", source_filepath.to_str().unwrap(), "-"]) + ).unwrap(); let app_data_dir = bak9::os::user_app_data_dir(true, bak9::BAK9.into()) .expect("Failed to get user app data directory"); diff --git a/tools/build-release.bash b/tools/build-release.bash index 4b95c1e..da21026 100755 --- a/tools/build-release.bash +++ b/tools/build-release.bash @@ -6,9 +6,58 @@ source "${PROJECT_DIR}/tools/common.lib.bash" echo "began building releases" -for target in "${RELEASE_TARGETS[@]}"; do - echo "building release: ${target}" +for target in "${LINUX_RELEASE_TARGETS[@]}"; do + echo "building linux release: ${target}" cross build --release --target "${target}" done +for target in "${WINDOWS_CROSS_RELEASE_TARGETS[@]}"; do + echo "building windows cross release: ${target}" + cross build --release --target "${target}" +done + +for target in "${MACOS_CROSS_RELEASE_TARGETS[@]}"; do + echo "building macos cross release: ${target}" + cross build --release --target "${target}" +done + +if [ -f "${PROJECT_DIR}/pkg.cfg" ]; then + source "${PROJECT_DIR}/pkg.cfg" + + echo "remote building native windows releases" + + mkdir -p "${PROJECT_DIR}/target/pkg/msi" + + for target in "${WINDOWS_NATIVE_RELEASE_TARGETS[@]}"; do + echo "remote building native windows release: ${target}" + ssh "$WINDOWS_SSH_HOST" "cd "$WINDOWS_SSH_PROJECT_DIR" ; cargo build --release --target="${target}"" + echo "remote building msi: ${target}" + ssh "$WINDOWS_SSH_HOST" "cd "$WINDOWS_SSH_PROJECT_DIR" ; cargo wix" + mkdir -p "${PROJECT_DIR}/target/${target}/release" + echo "downloading build artifacts: ${target}" + scp "${WINDOWS_SSH_HOST}:${WINDOWS_SSH_PROJECT_DIR}/target/${target}/release/${CARGO_BIN_NAME}.exe" "${PROJECT_DIR}/target/${target}/release" + done + + scp "${WINDOWS_SSH_HOST}:${WINDOWS_SSH_PROJECT_DIR}/target/wix/*.msi" "${PROJECT_DIR}/target/pkg/msi" + + for msi in "${PROJECT_DIR}/target/pkg/msi"/*.msi; do + sha256sum -b "${msi}" > "${msi}.sha256" + done + + echo "finished remote building native windows releases" + echo "remote building native macos releases" + + for target in "${MACOS_NATIVE_RELEASE_TARGETS[@]}"; do + echo "remote building native macos release: ${target}" + ssh "$MACOS_SSH_HOST" "cd "$MACOS_SSH_PROJECT_DIR" && cargo build --release --target="${target}"" + mkdir -p "${PROJECT_DIR}/target/${target}/release" + echo "downloading build artifacts: ${target}" + scp "${MACOS_SSH_HOST}:${MACOS_SSH_PROJECT_DIR}/target/${target}/release/${CARGO_BIN_NAME}" "${PROJECT_DIR}/target/${target}/release" + done + + echo "finished remote building native macos releases" +else + echo "pkg.cfg not found, skipping remote building of native releases" +fi + echo "finished building releases" diff --git a/tools/common.lib.bash b/tools/common.lib.bash index 6855847..d42122e 100644 --- a/tools/common.lib.bash +++ b/tools/common.lib.bash @@ -5,13 +5,19 @@ set -euo pipefail TARGET_LINUX_X86_64="x86_64-unknown-linux-gnu" TARGET_LINUX_ARM_64="aarch64-unknown-linux-gnu" TARGET_LINUX_ARM_V7="armv7-unknown-linux-gnueabihf" -TARGET_WINDOWS_X86_64="x86_64-pc-windows-gnu" +TARGET_WINDOWS_X86_64_MSVC="x86_64-pc-windows-msvc" +TARGET_WINDOWS_ARM_64="aarch64-pc-windows-msvc" +TARGET_WINDOWS_X86_64_GNU="x86_64-pc-windows-gnu" +TARGET_MACOS_ARM_64="aarch64-apple-darwin" RELEASE_TARGETS=( "${TARGET_LINUX_X86_64}" "${TARGET_LINUX_ARM_64}" "${TARGET_LINUX_ARM_V7}" - "${TARGET_WINDOWS_X86_64}" + "${TARGET_WINDOWS_X86_64_MSVC}" + "${TARGET_WINDOWS_ARM_64}" + "${TARGET_WINDOWS_X86_64_GNU}" + "${TARGET_MACOS_ARM_64}" ) LINUX_RELEASE_TARGETS=( @@ -20,8 +26,23 @@ LINUX_RELEASE_TARGETS=( "${TARGET_LINUX_ARM_V7}" ) +WINDOWS_NATIVE_RELEASE_TARGETS=( + "${TARGET_WINDOWS_X86_64_MSVC}" +) + +WINDOWS_CROSS_RELEASE_TARGETS=( + "${TARGET_WINDOWS_X86_64_GNU}" +) + +MACOS_CROSS_RELEASE_TARGETS=() + +MACOS_NATIVE_RELEASE_TARGETS=( + "${TARGET_MACOS_ARM_64}" +) CARGO_NAME="$(grep -m1 '^name' "${PROJECT_DIR}/Cargo.toml" | cut -d '"' -f 2)" CARGO_VERSION="$(grep '^version' "${PROJECT_DIR}/Cargo.toml" | cut -d '"' -f 2)" CARGO_VERSION_EXT="${CARGO_VERSION}-1" CARGO_BIN_NAME="$(sed -n '/\[\[bin\]\]/,$p' "${PROJECT_DIR}/Cargo.toml" | grep '^name' | cut -d '"' -f 2)" + +GIT_BRANCH="$(git rev-parse --abbrev-ref HEAD)" diff --git a/tools/package-all.bash b/tools/package-all.bash index 4b5fdb1..8a58069 100755 --- a/tools/package-all.bash +++ b/tools/package-all.bash @@ -4,12 +4,12 @@ set -euo pipefail PROJECT_DIR="$(realpath "$(dirname "$0")/..")" -echo "began packaging everything" +echo "began packaging linux releases" "${PROJECT_DIR}/tools/package-tarball.bash" "${PROJECT_DIR}/tools/package-debian.bash" "${PROJECT_DIR}/tools/package-rpm.bash" "${PROJECT_DIR}/tools/package-snap.bash" -echo "finished packaging everything" +echo "finished packaging linux releases" diff --git a/tools/package-debian.bash b/tools/package-debian.bash index c39a290..f15da35 100755 --- a/tools/package-debian.bash +++ b/tools/package-debian.bash @@ -12,12 +12,9 @@ DEB_DIR="${PROJECT_DIR}/target/pkg/debian" rm -rf "${DEB_DIR}" mkdir -p "${DEB_DIR}" -for target in "${RELEASE_TARGETS[@]}"; do - [[ "$target" != *"linux"* ]] && - continue - +for target in "${LINUX_RELEASE_TARGETS[@]}"; do echo "packaging .deb: ${target}" - cargo deb --target "${target}" --no-build --output="${DEB_DIR}" + cargo deb --target "${target}" --no-build --no-strip --output="${DEB_DIR}" done for deb in "${DEB_DIR}"/*.deb; do diff --git a/tools/package-rpm.bash b/tools/package-rpm.bash index e87d29a..54b9c41 100755 --- a/tools/package-rpm.bash +++ b/tools/package-rpm.bash @@ -14,12 +14,8 @@ mkdir -p "${RPM_DIR}" cd "${PROJECT_DIR}" -for target in "${RELEASE_TARGETS[@]}"; do +for target in "${LINUX_RELEASE_TARGETS[@]}"; do echo "packaging rpm: ${target}" - - [[ "$target" != *"linux"* ]] && - continue - cargo generate-rpm --target "${target}" --output "${RPM_DIR}" done diff --git a/tools/package-tarball.bash b/tools/package-tarball.bash index d16625c..b126494 100755 --- a/tools/package-tarball.bash +++ b/tools/package-tarball.bash @@ -22,19 +22,20 @@ echo "packaging tarball: source" git archive --format tar.gz --prefix "${CARGO_NAME}_${CARGO_VERSION}/" HEAD > "${TARBALL_DIR}/${CARGO_NAME}_${CARGO_VERSION}.tar.gz" for target in "${RELEASE_TARGETS[@]}"; do - echo "packaging tarball: ${target}" package_dir_name="${CARGO_NAME}_${CARGO_VERSION}_${target//_/-}" package_dir="${TARBALL_DIR}/${package_dir_name}" - mkdir -p "${package_dir}" - - rsync -a "${TARBALL_TEMPLATE_DIR}/" "${package_dir}" + bin_path="${PROJECT_DIR}/target/${target}/release/${CARGO_BIN_NAME}" - if [[ "$target" != *"windows"* ]]; then - cp "${PROJECT_DIR}/target/${target}/release/${CARGO_BIN_NAME}" "${package_dir}" - else - cp "${PROJECT_DIR}/target/${target}/release/${CARGO_BIN_NAME}.exe" "${package_dir}" + if [[ "$target" == *"windows"* ]]; then + bin_path="${PROJECT_DIR}/target/${target}/release/${CARGO_BIN_NAME}.exe" fi + [ -f "${bin_path}" ] || continue + echo "packaging tarball: ${target}" + + mkdir -p "${package_dir}" + rsync -a "${TARBALL_TEMPLATE_DIR}/" "${package_dir}" + cp "${bin_path}" "${package_dir}" cd "${package_dir}/.." tar cf "${package_dir_name}.tar.xz" --use-compress-program='xz -T0' "${package_dir_name}" rm -rf "${package_dir}" diff --git a/tools/release.bash b/tools/release.bash index 4a31bbe..7592d5b 100755 --- a/tools/release.bash +++ b/tools/release.bash @@ -3,8 +3,27 @@ set -euo pipefail PROJECT_DIR="$(realpath "$(dirname "$0")/..")" +echo "began cleaning" + +cd $PROJECT_DIR +cargo clean + +if [ -f "${PROJECT_DIR}/pkg.cfg" ]; then + source "${PROJECT_DIR}/pkg.cfg" + + ssh "$WINDOWS_SSH_HOST" "cd "$WINDOWS_SSH_PROJECT_DIR" ; cargo clean" + ssh "$MACOS_SSH_HOST" "cd "$MACOS_SSH_PROJECT_DIR" && cargo clean" +fi + +echo "finished cleaning" + echo "began releasing" +cargo build +cargo test +cargo build --release +cargo test --release + "${PROJECT_DIR}/tools/build-release.bash" "${PROJECT_DIR}/tools/package-all.bash"