mirror of
https://codeberg.org/Toasterson/ips.git
synced 2026-04-10 21:30:41 +00:00
Add xtask for build and test automation, replacing legacy scripts, and integrate commands for environment setup, build, test, and formatting. Update documentation for cargo-xtask usage.
This commit is contained in:
parent
56b50c755a
commit
40560e5960
5 changed files with 298 additions and 15 deletions
|
|
@ -130,40 +130,41 @@ cargo test -p <crate_name>
|
|||
|
||||
### Setting Up Test Environment
|
||||
|
||||
The project includes a script to set up the test environment for repository tests:
|
||||
The project uses cargo-xtask for automation tasks, including setting up the test environment:
|
||||
```bash
|
||||
./setup_test_env.sh
|
||||
cargo xtask setup-test-env
|
||||
```
|
||||
|
||||
This script:
|
||||
This command:
|
||||
1. Creates test directories in `/tmp/pkg6_test`
|
||||
2. Compiles the applications
|
||||
3. Creates a prototype directory structure with sample files
|
||||
4. Creates package manifests for testing
|
||||
|
||||
The legacy script `./setup_test_env.sh` is still available but is being phased out in favor of cargo-xtask.
|
||||
|
||||
### Writing Tests
|
||||
|
||||
- Unit tests should be placed in the same file as the code they're testing, in a `mod tests` block
|
||||
- Integration tests should be placed in the `tests` directory of each crate
|
||||
- End-to-end tests should use the test environment set up by `setup_test_env.sh`
|
||||
- End-to-end tests should use the test environment set up by `cargo xtask setup-test-env`
|
||||
|
||||
## Build Guidelines
|
||||
|
||||
### Building the Project
|
||||
|
||||
To build the entire project:
|
||||
Using cargo directly:
|
||||
```bash
|
||||
cargo build
|
||||
cargo build # Build the entire project
|
||||
cargo build -p <crate_name> # Build a specific crate
|
||||
cargo build --release # Build with optimizations for release
|
||||
```
|
||||
|
||||
To build a specific crate:
|
||||
Using cargo-xtask:
|
||||
```bash
|
||||
cargo build -p <crate_name>
|
||||
```
|
||||
|
||||
To build with optimizations for release:
|
||||
```bash
|
||||
cargo build --release
|
||||
cargo xtask build # Build the entire project
|
||||
cargo xtask build -p <crate_name> # Build a specific crate
|
||||
cargo xtask build -r # Build with optimizations for release
|
||||
```
|
||||
|
||||
### Build Order
|
||||
|
|
@ -180,6 +181,36 @@ The crates are built in the following order (as specified in the workspace Cargo
|
|||
|
||||
This order is important as it reflects the dependency hierarchy, with `libips` being the foundation that other crates build upon.
|
||||
|
||||
## Cargo-xtask
|
||||
|
||||
The project uses [cargo-xtask](https://github.com/matklad/cargo-xtask) for automation of tests and builds. This approach allows us to write build scripts and automation tasks in Rust instead of shell scripts, making them more maintainable and cross-platform.
|
||||
|
||||
### Available Commands
|
||||
|
||||
The following commands are available through cargo-xtask:
|
||||
|
||||
```bash
|
||||
cargo xtask setup-test-env # Set up the test environment for repository tests
|
||||
cargo xtask build # Build the project
|
||||
cargo xtask build -r # Build with release optimizations
|
||||
cargo xtask build -p <crate> # Build a specific crate
|
||||
cargo xtask test # Run tests
|
||||
cargo xtask test -r # Run tests with release optimizations
|
||||
cargo xtask test -p <crate> # Run tests for a specific crate
|
||||
cargo xtask fmt # Format code using cargo fmt
|
||||
cargo xtask clippy # Run clippy for code quality checks
|
||||
cargo xtask clean # Clean build artifacts
|
||||
```
|
||||
|
||||
### Adding New Commands
|
||||
|
||||
To add a new command to cargo-xtask:
|
||||
|
||||
1. Edit the `xtask/src/main.rs` file
|
||||
2. Add a new variant to the `Commands` enum
|
||||
3. Implement a function for the new command
|
||||
4. Add a match arm in the `main` function to call your new function
|
||||
|
||||
## Code Style Guidelines
|
||||
|
||||
### General Guidelines
|
||||
|
|
|
|||
12
Cargo.lock
generated
12
Cargo.lock
generated
|
|
@ -99,9 +99,9 @@ dependencies = [
|
|||
|
||||
[[package]]
|
||||
name = "anyhow"
|
||||
version = "1.0.70"
|
||||
version = "1.0.98"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "7de8ce5e0f9f8d88245311066a578d72b7af3e7088f32783804676302df237e4"
|
||||
checksum = "e16d2d3311acee920a9eb8d33b8cbc1787ce4a264e85f964c2404b969bdcd487"
|
||||
|
||||
[[package]]
|
||||
name = "arc-swap"
|
||||
|
|
@ -2987,6 +2987,14 @@ dependencies = [
|
|||
"bitflags 2.9.1",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "xtask"
|
||||
version = "0.1.0"
|
||||
dependencies = [
|
||||
"anyhow",
|
||||
"clap 4.5.41",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "zerocopy"
|
||||
version = "0.8.26"
|
||||
|
|
|
|||
|
|
@ -9,6 +9,7 @@ members = [
|
|||
"specfile",
|
||||
"ports",
|
||||
"crates/*",
|
||||
"xtask",
|
||||
]
|
||||
|
||||
resolver = "2"
|
||||
10
xtask/Cargo.toml
Normal file
10
xtask/Cargo.toml
Normal file
|
|
@ -0,0 +1,10 @@
|
|||
[package]
|
||||
name = "xtask"
|
||||
version = "0.1.0"
|
||||
edition = "2021"
|
||||
description = "Build and test automation for IPS"
|
||||
publish = false
|
||||
|
||||
[dependencies]
|
||||
anyhow = "1.0.75"
|
||||
clap = { version = "4.4.6", features = ["derive"] }
|
||||
233
xtask/src/main.rs
Normal file
233
xtask/src/main.rs
Normal file
|
|
@ -0,0 +1,233 @@
|
|||
use anyhow::{Context, Result};
|
||||
use clap::{Parser, Subcommand};
|
||||
use std::fs::{self, File};
|
||||
use std::io::Write;
|
||||
use std::path::Path;
|
||||
use std::process::Command;
|
||||
|
||||
// Constants
|
||||
const TEST_BASE_DIR: &str = "/tmp/pkg6_test";
|
||||
const PROTOTYPE_DIR: &str = "/tmp/pkg6_test/prototype";
|
||||
const MANIFEST_DIR: &str = "/tmp/pkg6_test/manifests";
|
||||
|
||||
#[derive(Parser)]
|
||||
#[command(author, version, about, long_about = None)]
|
||||
struct Cli {
|
||||
#[command(subcommand)]
|
||||
command: Commands,
|
||||
}
|
||||
|
||||
#[derive(Subcommand)]
|
||||
enum Commands {
|
||||
/// Set up the test environment for repository tests
|
||||
SetupTestEnv,
|
||||
|
||||
/// Build the project
|
||||
Build {
|
||||
/// Build with release optimizations
|
||||
#[arg(short, long)]
|
||||
release: bool,
|
||||
|
||||
/// Specific crate to build
|
||||
#[arg(short, long)]
|
||||
package: Option<String>,
|
||||
},
|
||||
|
||||
/// Run tests
|
||||
Test {
|
||||
/// Run tests with release optimizations
|
||||
#[arg(short, long)]
|
||||
release: bool,
|
||||
|
||||
/// Specific crate to test
|
||||
#[arg(short, long)]
|
||||
package: Option<String>,
|
||||
},
|
||||
|
||||
/// Format code
|
||||
Fmt,
|
||||
|
||||
/// Run clippy
|
||||
Clippy,
|
||||
|
||||
/// Clean build artifacts
|
||||
Clean,
|
||||
}
|
||||
|
||||
fn main() -> Result<()> {
|
||||
let cli = Cli::parse();
|
||||
|
||||
match &cli.command {
|
||||
Commands::SetupTestEnv => setup_test_env(),
|
||||
Commands::Build { release, package } => build(release, package),
|
||||
Commands::Test { release, package } => test(release, package),
|
||||
Commands::Fmt => fmt(),
|
||||
Commands::Clippy => clippy(),
|
||||
Commands::Clean => clean(),
|
||||
}
|
||||
}
|
||||
|
||||
/// Set up the test environment for repository tests
|
||||
fn setup_test_env() -> Result<()> {
|
||||
println!("Setting up test environment...");
|
||||
|
||||
// Clean up any existing test directories
|
||||
if Path::new(TEST_BASE_DIR).exists() {
|
||||
println!("Cleaning up existing test directory...");
|
||||
fs::remove_dir_all(TEST_BASE_DIR).context("Failed to remove existing test directory")?;
|
||||
}
|
||||
|
||||
// Create test directories
|
||||
println!("Creating test directories...");
|
||||
fs::create_dir_all(PROTOTYPE_DIR).context("Failed to create prototype directory")?;
|
||||
fs::create_dir_all(MANIFEST_DIR).context("Failed to create manifest directory")?;
|
||||
|
||||
// Compile the applications
|
||||
println!("Compiling applications...");
|
||||
Command::new("cargo")
|
||||
.arg("build")
|
||||
.status()
|
||||
.context("Failed to compile applications")?;
|
||||
|
||||
// Create a simple prototype directory structure with some files
|
||||
println!("Creating prototype directory structure...");
|
||||
|
||||
// Create some directories
|
||||
fs::create_dir_all(format!("{}/usr/bin", PROTOTYPE_DIR)).context("Failed to create usr/bin directory")?;
|
||||
fs::create_dir_all(format!("{}/usr/share/doc/example", PROTOTYPE_DIR)).context("Failed to create usr/share/doc/example directory")?;
|
||||
fs::create_dir_all(format!("{}/etc/config", PROTOTYPE_DIR)).context("Failed to create etc/config directory")?;
|
||||
|
||||
// Create some files
|
||||
let hello_script = "#!/bin/sh\necho 'Hello, World!'";
|
||||
let mut hello_file = File::create(format!("{}/usr/bin/hello", PROTOTYPE_DIR)).context("Failed to create hello script")?;
|
||||
hello_file.write_all(hello_script.as_bytes()).context("Failed to write hello script")?;
|
||||
|
||||
// Make the script executable
|
||||
#[cfg(unix)]
|
||||
{
|
||||
use std::os::unix::fs::PermissionsExt;
|
||||
let mut perms = fs::metadata(format!("{}/usr/bin/hello", PROTOTYPE_DIR))
|
||||
.context("Failed to get hello script metadata")?
|
||||
.permissions();
|
||||
perms.set_mode(0o755);
|
||||
fs::set_permissions(format!("{}/usr/bin/hello", PROTOTYPE_DIR), perms)
|
||||
.context("Failed to set hello script permissions")?;
|
||||
}
|
||||
|
||||
let readme_content = "This is an example document.";
|
||||
let mut readme_file = File::create(format!("{}/usr/share/doc/example/README.txt", PROTOTYPE_DIR))
|
||||
.context("Failed to create README.txt")?;
|
||||
readme_file.write_all(readme_content.as_bytes()).context("Failed to write README.txt")?;
|
||||
|
||||
let config_content = "# Example configuration file\nvalue=42";
|
||||
let mut config_file = File::create(format!("{}/etc/config/example.conf", PROTOTYPE_DIR))
|
||||
.context("Failed to create example.conf")?;
|
||||
config_file.write_all(config_content.as_bytes()).context("Failed to write example.conf")?;
|
||||
|
||||
// Create a simple manifest
|
||||
println!("Creating package manifest...");
|
||||
let example_manifest = r#"set name=pkg.fmri value=pkg://test/example@1.0.0
|
||||
set name=pkg.summary value="Example package for testing"
|
||||
set name=pkg.description value="This is an example package used for testing the repository implementation."
|
||||
set name=info.classification value="org.opensolaris.category.2008:System/Core"
|
||||
set name=variant.arch value=i386 value=sparc
|
||||
file path=usr/bin/hello mode=0755 owner=root group=bin
|
||||
file path=usr/share/doc/example/README.txt mode=0644 owner=root group=bin
|
||||
file path=etc/config/example.conf mode=0644 owner=root group=bin preserve=true
|
||||
dir path=usr/bin mode=0755 owner=root group=bin
|
||||
dir path=usr/share/doc/example mode=0755 owner=root group=bin
|
||||
dir path=etc/config mode=0755 owner=root group=sys
|
||||
"#;
|
||||
|
||||
let mut example_file = File::create(format!("{}/example.p5m", MANIFEST_DIR))
|
||||
.context("Failed to create example.p5m")?;
|
||||
example_file.write_all(example_manifest.as_bytes()).context("Failed to write example.p5m")?;
|
||||
|
||||
// Create a second manifest for testing multiple packages
|
||||
let example2_manifest = r#"set name=pkg.fmri value=pkg://test/example2@1.0.0
|
||||
set name=pkg.summary value="Second example package for testing"
|
||||
set name=pkg.description value="This is a second example package used for testing the repository implementation."
|
||||
set name=info.classification value="org.opensolaris.category.2008:System/Core"
|
||||
set name=variant.arch value=i386 value=sparc
|
||||
file path=usr/bin/hello mode=0755 owner=root group=bin
|
||||
file path=usr/share/doc/example/README.txt mode=0644 owner=root group=bin
|
||||
dir path=usr/bin mode=0755 owner=root group=bin
|
||||
dir path=usr/share/doc/example mode=0755 owner=root group=bin
|
||||
"#;
|
||||
|
||||
let mut example2_file = File::create(format!("{}/example2.p5m", MANIFEST_DIR))
|
||||
.context("Failed to create example2.p5m")?;
|
||||
example2_file.write_all(example2_manifest.as_bytes()).context("Failed to write example2.p5m")?;
|
||||
|
||||
println!("Test environment setup complete!");
|
||||
println!("Prototype directory: {}", PROTOTYPE_DIR);
|
||||
println!("Manifest directory: {}", MANIFEST_DIR);
|
||||
|
||||
Ok(())
|
||||
}
|
||||
|
||||
/// Build the project
|
||||
fn build(release: &bool, package: &Option<String>) -> Result<()> {
|
||||
let mut cmd = Command::new("cargo");
|
||||
cmd.arg("build");
|
||||
|
||||
if *release {
|
||||
cmd.arg("--release");
|
||||
}
|
||||
|
||||
if let Some(pkg) = package {
|
||||
cmd.args(["--package", pkg]);
|
||||
}
|
||||
|
||||
cmd.status().context("Failed to build project")?;
|
||||
|
||||
Ok(())
|
||||
}
|
||||
|
||||
/// Run tests
|
||||
fn test(release: &bool, package: &Option<String>) -> Result<()> {
|
||||
let mut cmd = Command::new("cargo");
|
||||
cmd.arg("test");
|
||||
|
||||
if *release {
|
||||
cmd.arg("--release");
|
||||
}
|
||||
|
||||
if let Some(pkg) = package {
|
||||
cmd.args(["--package", pkg]);
|
||||
}
|
||||
|
||||
cmd.status().context("Failed to run tests")?;
|
||||
|
||||
Ok(())
|
||||
}
|
||||
|
||||
/// Format code
|
||||
fn fmt() -> Result<()> {
|
||||
Command::new("cargo")
|
||||
.arg("fmt")
|
||||
.status()
|
||||
.context("Failed to format code")?;
|
||||
|
||||
Ok(())
|
||||
}
|
||||
|
||||
/// Run clippy
|
||||
fn clippy() -> Result<()> {
|
||||
Command::new("cargo")
|
||||
.args(["clippy", "--all-targets", "--all-features", "--", "-D", "warnings"])
|
||||
.status()
|
||||
.context("Failed to run clippy")?;
|
||||
|
||||
Ok(())
|
||||
}
|
||||
|
||||
/// Clean build artifacts
|
||||
fn clean() -> Result<()> {
|
||||
Command::new("cargo")
|
||||
.arg("clean")
|
||||
.status()
|
||||
.context("Failed to clean build artifacts")?;
|
||||
|
||||
Ok(())
|
||||
}
|
||||
Loading…
Add table
Reference in a new issue