refactor: Move templates to their own crate (#83)
This PR logically separates out parts of the code to their own crates. This will be useful for future Tauri App development.
This commit is contained in:
parent
ce8f889dc2
commit
910e0434b6
34 changed files with 620 additions and 512 deletions
120
Cargo.lock
generated
120
Cargo.lock
generated
|
|
@ -130,9 +130,9 @@ dependencies = [
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "anyhow"
|
name = "anyhow"
|
||||||
version = "1.0.79"
|
version = "1.0.80"
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "080e9890a082662b09c1ad45f567faeeb47f22b5fb23895fbe1e651e718e25ca"
|
checksum = "5ad32ce52e4161730f7098c077cd2ed6229b5804ccf99e5366be1ab72a98b4e1"
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "askama"
|
name = "askama"
|
||||||
|
|
@ -147,7 +147,7 @@ dependencies = [
|
||||||
"percent-encoding",
|
"percent-encoding",
|
||||||
"serde",
|
"serde",
|
||||||
"serde_json",
|
"serde_json",
|
||||||
"serde_yaml 0.9.31",
|
"serde_yaml 0.9.32",
|
||||||
]
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
|
|
@ -281,31 +281,27 @@ name = "blue-build"
|
||||||
version = "0.8.0"
|
version = "0.8.0"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"anyhow",
|
"anyhow",
|
||||||
"askama",
|
"blue-build-recipe",
|
||||||
"chrono",
|
"blue-build-template",
|
||||||
|
"blue-build-utils",
|
||||||
"clap",
|
"clap",
|
||||||
"clap-verbosity-flag",
|
"clap-verbosity-flag",
|
||||||
"clap_complete",
|
"clap_complete",
|
||||||
"clap_complete_nushell",
|
"clap_complete_nushell",
|
||||||
"colorized",
|
"colorized",
|
||||||
"derive_builder",
|
|
||||||
"directories",
|
|
||||||
"dunce",
|
"dunce",
|
||||||
"env_logger",
|
"env_logger",
|
||||||
"format_serde_error",
|
|
||||||
"futures-util",
|
"futures-util",
|
||||||
"fuzzy-matcher",
|
"fuzzy-matcher",
|
||||||
"indexmap 2.2.3",
|
|
||||||
"log",
|
"log",
|
||||||
"open",
|
"open",
|
||||||
"os_info",
|
"os_info",
|
||||||
"podman-api",
|
"podman-api",
|
||||||
"process_control",
|
|
||||||
"requestty",
|
"requestty",
|
||||||
"rusty-hook",
|
"rusty-hook",
|
||||||
"serde",
|
"serde",
|
||||||
"serde_json",
|
"serde_json",
|
||||||
"serde_yaml 0.9.31",
|
"serde_yaml 0.9.32",
|
||||||
"shadow-rs",
|
"shadow-rs",
|
||||||
"signal-hook",
|
"signal-hook",
|
||||||
"signal-hook-tokio",
|
"signal-hook-tokio",
|
||||||
|
|
@ -315,6 +311,51 @@ dependencies = [
|
||||||
"urlencoding",
|
"urlencoding",
|
||||||
"users",
|
"users",
|
||||||
"uuid",
|
"uuid",
|
||||||
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "blue-build-recipe"
|
||||||
|
version = "0.8.0"
|
||||||
|
dependencies = [
|
||||||
|
"anyhow",
|
||||||
|
"blue-build-utils",
|
||||||
|
"chrono",
|
||||||
|
"format_serde_error",
|
||||||
|
"indexmap 2.2.3",
|
||||||
|
"log",
|
||||||
|
"serde",
|
||||||
|
"serde_json",
|
||||||
|
"serde_yaml 0.9.32",
|
||||||
|
"typed-builder",
|
||||||
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "blue-build-template"
|
||||||
|
version = "0.8.0"
|
||||||
|
dependencies = [
|
||||||
|
"askama",
|
||||||
|
"blue-build-recipe",
|
||||||
|
"blue-build-utils",
|
||||||
|
"log",
|
||||||
|
"serde",
|
||||||
|
"serde_json",
|
||||||
|
"serde_yaml 0.9.32",
|
||||||
|
"typed-builder",
|
||||||
|
"uuid",
|
||||||
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "blue-build-utils"
|
||||||
|
version = "0.8.0"
|
||||||
|
dependencies = [
|
||||||
|
"anyhow",
|
||||||
|
"directories",
|
||||||
|
"format_serde_error",
|
||||||
|
"log",
|
||||||
|
"process_control",
|
||||||
|
"serde",
|
||||||
|
"serde_json",
|
||||||
|
"serde_yaml 0.9.32",
|
||||||
"which",
|
"which",
|
||||||
]
|
]
|
||||||
|
|
||||||
|
|
@ -649,21 +690,6 @@ dependencies = [
|
||||||
"cfg-if",
|
"cfg-if",
|
||||||
]
|
]
|
||||||
|
|
||||||
[[package]]
|
|
||||||
name = "crossbeam-channel"
|
|
||||||
version = "0.5.11"
|
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
|
||||||
checksum = "176dc175b78f56c0f321911d9c8eb2b77a78a4860b9c19db83835fea1a46649b"
|
|
||||||
dependencies = [
|
|
||||||
"crossbeam-utils",
|
|
||||||
]
|
|
||||||
|
|
||||||
[[package]]
|
|
||||||
name = "crossbeam-utils"
|
|
||||||
version = "0.8.19"
|
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
|
||||||
checksum = "248e3bacc7dc6baa3b21e405ee045c3047101a49145e7e9eca583ab4c2ca5345"
|
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "crossterm"
|
name = "crossterm"
|
||||||
version = "0.25.0"
|
version = "0.25.0"
|
||||||
|
|
@ -859,37 +885,6 @@ dependencies = [
|
||||||
"serde",
|
"serde",
|
||||||
]
|
]
|
||||||
|
|
||||||
[[package]]
|
|
||||||
name = "derive_builder"
|
|
||||||
version = "0.13.1"
|
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
|
||||||
checksum = "8f59169f400d8087f238c5c0c7db6a28af18681717f3b623227d92f397e938c7"
|
|
||||||
dependencies = [
|
|
||||||
"derive_builder_macro",
|
|
||||||
]
|
|
||||||
|
|
||||||
[[package]]
|
|
||||||
name = "derive_builder_core"
|
|
||||||
version = "0.13.1"
|
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
|
||||||
checksum = "a4ec317cc3e7ef0928b0ca6e4a634a4d6c001672ae210438cf114a83e56b018d"
|
|
||||||
dependencies = [
|
|
||||||
"darling 0.14.4",
|
|
||||||
"proc-macro2",
|
|
||||||
"quote",
|
|
||||||
"syn 1.0.109",
|
|
||||||
]
|
|
||||||
|
|
||||||
[[package]]
|
|
||||||
name = "derive_builder_macro"
|
|
||||||
version = "0.13.1"
|
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
|
||||||
checksum = "870368c3fb35b8031abb378861d4460f573b92238ec2152c927a21f77e3e0127"
|
|
||||||
dependencies = [
|
|
||||||
"derive_builder_core",
|
|
||||||
"syn 1.0.109",
|
|
||||||
]
|
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "digest"
|
name = "digest"
|
||||||
version = "0.10.7"
|
version = "0.10.7"
|
||||||
|
|
@ -2518,14 +2513,13 @@ dependencies = [
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "process_control"
|
name = "process_control"
|
||||||
version = "4.0.3"
|
version = "4.1.0"
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "32e056a69288d0a211f4c74c48391c6eb86e714fdcb9dc58a9f34302da9c20bf"
|
checksum = "4d18334c4a4b2770ee894e63cf466d5a9ea449cf29e321101b0b135a747afb6f"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"crossbeam-channel",
|
|
||||||
"libc",
|
"libc",
|
||||||
"signal-hook",
|
"signal-hook",
|
||||||
"windows-sys 0.48.0",
|
"windows-sys 0.52.0",
|
||||||
]
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
|
|
@ -3073,9 +3067,9 @@ dependencies = [
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "serde_yaml"
|
name = "serde_yaml"
|
||||||
version = "0.9.31"
|
version = "0.9.32"
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "adf8a49373e98a4c5f0ceb5d05aa7c648d75f63774981ed95b7c7443bbd50c6e"
|
checksum = "8fd075d994154d4a774f95b51fb96bdc2832b0ea48425c92546073816cda1f2f"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"indexmap 2.2.3",
|
"indexmap 2.2.3",
|
||||||
"itoa",
|
"itoa",
|
||||||
|
|
|
||||||
82
Cargo.toml
82
Cargo.toml
|
|
@ -1,51 +1,80 @@
|
||||||
[package]
|
[workspace]
|
||||||
name = "blue-build"
|
members = [ "utils", "recipe","template"]
|
||||||
|
|
||||||
|
[workspace.package]
|
||||||
version = "0.8.0"
|
version = "0.8.0"
|
||||||
edition = "2021"
|
|
||||||
description = "A CLI tool built for creating Containerfile templates based on the Ublue Community Project"
|
description = "A CLI tool built for creating Containerfile templates based on the Ublue Community Project"
|
||||||
|
edition = "2021"
|
||||||
repository = "https://github.com/blue-build/cli"
|
repository = "https://github.com/blue-build/cli"
|
||||||
license = "Apache-2.0"
|
license = "Apache-2.0"
|
||||||
categories = ["command-line-utilities"]
|
categories = ["command-line-utilities"]
|
||||||
|
|
||||||
|
[workspace.dependencies]
|
||||||
|
anyhow = "1"
|
||||||
|
format_serde_error = "0.3.0"
|
||||||
|
log = "0.4"
|
||||||
|
serde = { version = "1", features = ["derive"] }
|
||||||
|
serde_json = "1"
|
||||||
|
serde_yaml = "0.9.30"
|
||||||
|
typed-builder = "0.18.1"
|
||||||
|
uuid = { version = "1.7.0", features = ["v4"] }
|
||||||
|
|
||||||
|
[workspace.lints.rust]
|
||||||
|
unsafe_code = "forbid"
|
||||||
|
|
||||||
|
[workspace.lints.clippy]
|
||||||
|
correctness = "warn"
|
||||||
|
suspicious = "warn"
|
||||||
|
perf = "warn"
|
||||||
|
style = "warn"
|
||||||
|
nursery = "warn"
|
||||||
|
|
||||||
|
[package]
|
||||||
|
name = "blue-build"
|
||||||
build = "build.rs"
|
build = "build.rs"
|
||||||
|
|
||||||
|
version.workspace = true
|
||||||
|
edition.workspace = true
|
||||||
|
description.workspace = true
|
||||||
|
repository.workspace = true
|
||||||
|
license.workspace = true
|
||||||
|
|
||||||
[dependencies]
|
[dependencies]
|
||||||
anyhow = "1"
|
blue-build-recipe = { path = "./recipe" }
|
||||||
askama = { version = "0.12", features = ["serde-json", "serde-yaml"] }
|
blue-build-template = { path = "./template" }
|
||||||
chrono = "0.4"
|
blue-build-utils = { path = "./utils" }
|
||||||
clap = { version = "4", features = ["derive", "cargo", "unicode"] }
|
clap = { version = "4", features = ["derive", "cargo", "unicode"] }
|
||||||
clap-verbosity-flag = "2"
|
clap-verbosity-flag = "2"
|
||||||
clap_complete = "4"
|
clap_complete = "4"
|
||||||
clap_complete_nushell = "4"
|
clap_complete_nushell = "4"
|
||||||
colorized = "1"
|
colorized = "1"
|
||||||
derive_builder = "0.13"
|
|
||||||
directories = "5"
|
|
||||||
env_logger = "0.11"
|
env_logger = "0.11"
|
||||||
format_serde_error = "0.3.0"
|
|
||||||
futures-util = { version = "0.3", optional = true }
|
|
||||||
fuzzy-matcher = "0.3"
|
fuzzy-matcher = "0.3"
|
||||||
indexmap = { version = "2", features = ["serde"] }
|
|
||||||
log = "0.4"
|
|
||||||
open = "5"
|
open = "5"
|
||||||
# update os module config and tests when upgrading os_info
|
os_info = "3.7" # update os module config and tests when upgrading os_info
|
||||||
os_info = "3.7"
|
|
||||||
podman-api = { version = "0.10.0", optional = true }
|
|
||||||
process_control = { version = "4.0.3", features = ["crossbeam-channel"] }
|
|
||||||
requestty = { version = "0.5", features = ["macros", "termion"] }
|
requestty = { version = "0.5", features = ["macros", "termion"] }
|
||||||
serde = { version = "1", features = ["derive"] }
|
shadow-rs = { version = "0.26" }
|
||||||
serde_json = "1"
|
urlencoding = "2.1.3"
|
||||||
serde_yaml = "0.9.30"
|
users = "0.11.0"
|
||||||
|
|
||||||
|
# Optional Dependencies
|
||||||
|
futures-util = { version = "0.3", optional = true }
|
||||||
|
podman-api = { version = "0.10.0", optional = true }
|
||||||
signal-hook = { version = "0.3.17", optional = true }
|
signal-hook = { version = "0.3.17", optional = true }
|
||||||
signal-hook-tokio = { version = "0.3.1", features = [
|
signal-hook-tokio = { version = "0.3.1", features = [
|
||||||
"futures-v0_3",
|
"futures-v0_3",
|
||||||
], optional = true }
|
], optional = true }
|
||||||
shadow-rs = { version = "0.26" }
|
|
||||||
sigstore = { version = "0.8.0", optional = true }
|
sigstore = { version = "0.8.0", optional = true }
|
||||||
tokio = { version = "1", features = ["full"], optional = true }
|
tokio = { version = "1", features = ["full"], optional = true }
|
||||||
typed-builder = "0.18.1"
|
|
||||||
urlencoding = "2.1.3"
|
# Workspace dependencies
|
||||||
users = "0.11.0"
|
anyhow.workspace = true
|
||||||
uuid = { version = "1.7.0", features = ["v4"] }
|
log.workspace = true
|
||||||
which = "6"
|
serde.workspace = true
|
||||||
|
serde_json.workspace = true
|
||||||
|
serde_yaml.workspace = true
|
||||||
|
typed-builder.workspace = true
|
||||||
|
uuid.workspace = true
|
||||||
|
|
||||||
[features]
|
[features]
|
||||||
default = []
|
default = []
|
||||||
|
|
@ -66,6 +95,9 @@ rusty-hook = "0.11.2"
|
||||||
shadow-rs = { version = "0.26.1", default-features = false }
|
shadow-rs = { version = "0.26.1", default-features = false }
|
||||||
dunce = "1.0.4"
|
dunce = "1.0.4"
|
||||||
|
|
||||||
|
[lints]
|
||||||
|
workspace = true
|
||||||
|
|
||||||
[profile.release]
|
[profile.release]
|
||||||
lto = true
|
lto = true
|
||||||
codegen-units = 1
|
codegen-units = 1
|
||||||
|
|
|
||||||
|
|
@ -54,7 +54,7 @@ common:
|
||||||
FROM ghcr.io/blue-build/earthly-lib/cargo-builder
|
FROM ghcr.io/blue-build/earthly-lib/cargo-builder
|
||||||
|
|
||||||
WORKDIR /app
|
WORKDIR /app
|
||||||
COPY --keep-ts --dir src/ templates/ /app
|
COPY --keep-ts --dir src/ template/ recipe/ utils/ /app
|
||||||
COPY --keep-ts Cargo.* /app
|
COPY --keep-ts Cargo.* /app
|
||||||
COPY --keep-ts *.md /app
|
COPY --keep-ts *.md /app
|
||||||
COPY --keep-ts LICENSE /app
|
COPY --keep-ts LICENSE /app
|
||||||
|
|
|
||||||
26
recipe/Cargo.toml
Normal file
26
recipe/Cargo.toml
Normal file
|
|
@ -0,0 +1,26 @@
|
||||||
|
[package]
|
||||||
|
name = "blue-build-recipe"
|
||||||
|
version.workspace = true
|
||||||
|
edition.workspace = true
|
||||||
|
description.workspace = true
|
||||||
|
repository.workspace = true
|
||||||
|
license.workspace = true
|
||||||
|
|
||||||
|
# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html
|
||||||
|
|
||||||
|
[dependencies]
|
||||||
|
blue-build-utils = { path = "../utils" }
|
||||||
|
chrono = "0.4"
|
||||||
|
indexmap = { version = "2", features = ["serde"] }
|
||||||
|
|
||||||
|
anyhow.workspace = true
|
||||||
|
format_serde_error.workspace = true
|
||||||
|
log.workspace = true
|
||||||
|
serde.workspace = true
|
||||||
|
serde_yaml.workspace = true
|
||||||
|
serde_json.workspace = true
|
||||||
|
typed-builder.workspace = true
|
||||||
|
|
||||||
|
[lints]
|
||||||
|
workspace = true
|
||||||
|
|
||||||
23
recipe/src/image_inspection.rs
Normal file
23
recipe/src/image_inspection.rs
Normal file
|
|
@ -0,0 +1,23 @@
|
||||||
|
use serde::Deserialize;
|
||||||
|
use serde_json::Value;
|
||||||
|
use std::collections::HashMap;
|
||||||
|
|
||||||
|
#[derive(Deserialize, Debug, Clone)]
|
||||||
|
pub struct ImageInspection {
|
||||||
|
#[serde(alias = "Labels")]
|
||||||
|
labels: HashMap<String, Value>,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl ImageInspection {
|
||||||
|
pub fn get_version(&self) -> Option<String> {
|
||||||
|
Some(
|
||||||
|
self.labels
|
||||||
|
.get("org.opencontainers.image.version")?
|
||||||
|
.as_str()
|
||||||
|
.map(std::string::ToString::to_string)?
|
||||||
|
.split('.')
|
||||||
|
.take(1)
|
||||||
|
.collect(),
|
||||||
|
)
|
||||||
|
}
|
||||||
|
}
|
||||||
11
recipe/src/lib.rs
Normal file
11
recipe/src/lib.rs
Normal file
|
|
@ -0,0 +1,11 @@
|
||||||
|
pub mod akmods_info;
|
||||||
|
pub mod image_inspection;
|
||||||
|
pub mod module;
|
||||||
|
pub mod module_ext;
|
||||||
|
pub mod recipe;
|
||||||
|
|
||||||
|
pub use akmods_info::*;
|
||||||
|
pub use image_inspection::*;
|
||||||
|
pub use module::*;
|
||||||
|
pub use module_ext::*;
|
||||||
|
pub use recipe::*;
|
||||||
133
recipe/src/module.rs
Normal file
133
recipe/src/module.rs
Normal file
|
|
@ -0,0 +1,133 @@
|
||||||
|
use std::{borrow::Cow, process};
|
||||||
|
|
||||||
|
use indexmap::IndexMap;
|
||||||
|
use log::{error, trace};
|
||||||
|
use serde::{Deserialize, Serialize};
|
||||||
|
use serde_yaml::Value;
|
||||||
|
use typed_builder::TypedBuilder;
|
||||||
|
|
||||||
|
use crate::{AkmodsInfo, ModuleExt};
|
||||||
|
|
||||||
|
#[derive(Serialize, Deserialize, Debug, Clone, TypedBuilder)]
|
||||||
|
pub struct Module<'a> {
|
||||||
|
#[builder(default, setter(into, strip_option))]
|
||||||
|
#[serde(rename = "type", skip_serializing_if = "Option::is_none")]
|
||||||
|
pub module_type: Option<Cow<'a, str>>,
|
||||||
|
|
||||||
|
#[builder(default, setter(into, strip_option))]
|
||||||
|
#[serde(rename = "from-file", skip_serializing_if = "Option::is_none")]
|
||||||
|
pub from_file: Option<Cow<'a, str>>,
|
||||||
|
|
||||||
|
#[builder(default, setter(into, strip_option))]
|
||||||
|
#[serde(skip_serializing_if = "Option::is_none")]
|
||||||
|
pub source: Option<Cow<'a, str>>,
|
||||||
|
|
||||||
|
#[serde(flatten)]
|
||||||
|
#[builder(default, setter(into))]
|
||||||
|
pub config: IndexMap<String, Value>,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl<'a> Module<'a> {
|
||||||
|
#[must_use]
|
||||||
|
pub fn get_modules(modules: &[Self]) -> Vec<Self> {
|
||||||
|
modules
|
||||||
|
.iter()
|
||||||
|
.flat_map(|module| {
|
||||||
|
module.from_file.as_ref().map_or_else(
|
||||||
|
|| vec![module.clone()],
|
||||||
|
|file_name| match ModuleExt::parse_module_from_file(file_name) {
|
||||||
|
Err(e) => {
|
||||||
|
error!("Failed to get module from {file_name}: {e}");
|
||||||
|
vec![]
|
||||||
|
}
|
||||||
|
Ok(module_ext) => Self::get_modules(&module_ext.modules),
|
||||||
|
},
|
||||||
|
)
|
||||||
|
})
|
||||||
|
.collect()
|
||||||
|
}
|
||||||
|
|
||||||
|
#[must_use]
|
||||||
|
pub fn get_module_type_list(&'a self, typ: &str, list_key: &str) -> Option<Vec<String>> {
|
||||||
|
if self.module_type.as_ref()? == typ {
|
||||||
|
Some(
|
||||||
|
self.config
|
||||||
|
.get(list_key)?
|
||||||
|
.as_sequence()?
|
||||||
|
.iter()
|
||||||
|
.filter_map(|t| Some(t.as_str()?.to_owned()))
|
||||||
|
.collect(),
|
||||||
|
)
|
||||||
|
} else {
|
||||||
|
None
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#[must_use]
|
||||||
|
pub fn get_containerfile_list(&'a self) -> Option<Vec<String>> {
|
||||||
|
self.get_module_type_list("containerfile", "containerfiles")
|
||||||
|
}
|
||||||
|
|
||||||
|
#[must_use]
|
||||||
|
pub fn get_containerfile_snippets(&'a self) -> Option<Vec<String>> {
|
||||||
|
self.get_module_type_list("containerfile", "snippets")
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn print_module_context(&'a self) -> String {
|
||||||
|
serde_json::to_string(self).unwrap_or_else(|e| {
|
||||||
|
error!("Failed to parse module!!!!!: {e}");
|
||||||
|
process::exit(1);
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn get_files_list(&'a self) -> Option<Vec<(String, String)>> {
|
||||||
|
Some(
|
||||||
|
self.config
|
||||||
|
.get("files")?
|
||||||
|
.as_sequence()?
|
||||||
|
.iter()
|
||||||
|
.filter_map(|entry| entry.as_mapping())
|
||||||
|
.flatten()
|
||||||
|
.filter_map(|(src, dest)| {
|
||||||
|
Some((
|
||||||
|
format!("./config/files/{}", src.as_str()?),
|
||||||
|
dest.as_str()?.to_string(),
|
||||||
|
))
|
||||||
|
})
|
||||||
|
.collect(),
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn generate_akmods_info(&'a self, os_version: &str) -> AkmodsInfo {
|
||||||
|
trace!("generate_akmods_base({self:#?}, {os_version})");
|
||||||
|
|
||||||
|
let base = self
|
||||||
|
.config
|
||||||
|
.get("base")
|
||||||
|
.map(|b| b.as_str().unwrap_or_default());
|
||||||
|
let nvidia_version = self
|
||||||
|
.config
|
||||||
|
.get("nvidia-version")
|
||||||
|
.map(|v| v.as_u64().unwrap_or_default());
|
||||||
|
|
||||||
|
AkmodsInfo::builder()
|
||||||
|
.images(match (base, nvidia_version) {
|
||||||
|
(Some(b), Some(nv)) if !b.is_empty() && nv > 0 => (
|
||||||
|
format!("akmods:{b}-{os_version}"),
|
||||||
|
Some(format!("akmods-nvidia:{b}-{os_version}-{nv}")),
|
||||||
|
),
|
||||||
|
(Some(b), _) if !b.is_empty() => (format!("akmods:{b}-{os_version}"), None),
|
||||||
|
(_, Some(nv)) if nv > 0 => (
|
||||||
|
format!("akmods:main-{os_version}"),
|
||||||
|
Some(format!("akmods-nvidia:main-{os_version}")),
|
||||||
|
),
|
||||||
|
_ => (format!("akmods:main-{os_version}"), None),
|
||||||
|
})
|
||||||
|
.stage_name(format!(
|
||||||
|
"{}{}",
|
||||||
|
base.unwrap_or("main"),
|
||||||
|
nvidia_version.map_or_else(String::default, |nv| format!("-{nv}"))
|
||||||
|
))
|
||||||
|
.build()
|
||||||
|
}
|
||||||
|
}
|
||||||
54
recipe/src/module_ext.rs
Normal file
54
recipe/src/module_ext.rs
Normal file
|
|
@ -0,0 +1,54 @@
|
||||||
|
use std::{borrow::Cow, collections::HashSet, fs, path::PathBuf};
|
||||||
|
|
||||||
|
use anyhow::Result;
|
||||||
|
use log::trace;
|
||||||
|
use serde::{Deserialize, Serialize};
|
||||||
|
use typed_builder::TypedBuilder;
|
||||||
|
|
||||||
|
use crate::{AkmodsInfo, Module};
|
||||||
|
|
||||||
|
#[derive(Default, Serialize, Clone, Deserialize, Debug, TypedBuilder)]
|
||||||
|
pub struct ModuleExt<'a> {
|
||||||
|
#[builder(default, setter(into))]
|
||||||
|
pub modules: Cow<'a, [Module<'a>]>,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl ModuleExt<'_> {
|
||||||
|
/// # Parse a module file returning a [`ModuleExt`]
|
||||||
|
///
|
||||||
|
/// # Errors
|
||||||
|
/// Can return an `anyhow` Error if the file cannot be read or deserialized
|
||||||
|
/// into a [`ModuleExt`]
|
||||||
|
pub fn parse_module_from_file(file_name: &str) -> Result<Self> {
|
||||||
|
let file_path = PathBuf::from("config").join(file_name);
|
||||||
|
let file_path = if file_path.is_absolute() {
|
||||||
|
file_path
|
||||||
|
} else {
|
||||||
|
std::env::current_dir()?.join(file_path)
|
||||||
|
};
|
||||||
|
|
||||||
|
let file = fs::read_to_string(file_path)?;
|
||||||
|
|
||||||
|
serde_yaml::from_str::<Self>(&file).map_or_else(
|
||||||
|
|_| -> Result<Self> {
|
||||||
|
let module = serde_yaml::from_str::<Module>(&file)
|
||||||
|
.map_err(blue_build_utils::serde_yaml_err(&file))?;
|
||||||
|
Ok(Self::builder().modules(vec![module]).build())
|
||||||
|
},
|
||||||
|
Ok,
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn get_akmods_info_list(&self, os_version: &str) -> Vec<AkmodsInfo> {
|
||||||
|
trace!("get_akmods_image_list({self:#?}, {os_version})");
|
||||||
|
|
||||||
|
let mut seen = HashSet::new();
|
||||||
|
|
||||||
|
self.modules
|
||||||
|
.iter()
|
||||||
|
.filter(|module| module.module_type.as_ref().is_some_and(|t| t == "akmods"))
|
||||||
|
.map(|module| module.generate_akmods_info(os_version))
|
||||||
|
.filter(|image| seen.insert(image.clone()))
|
||||||
|
.collect()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
@ -1,26 +1,16 @@
|
||||||
use std::{
|
use std::{borrow::Cow, env, fs, path::Path, process::Command};
|
||||||
borrow::Cow,
|
|
||||||
collections::{HashMap, HashSet},
|
|
||||||
env, fs,
|
|
||||||
path::{Path, PathBuf},
|
|
||||||
process::{self, Command},
|
|
||||||
};
|
|
||||||
|
|
||||||
use anyhow::Result;
|
use anyhow::Result;
|
||||||
|
use blue_build_utils::constants::*;
|
||||||
use chrono::Local;
|
use chrono::Local;
|
||||||
use format_serde_error::SerdeError;
|
use format_serde_error::SerdeError;
|
||||||
use indexmap::IndexMap;
|
use indexmap::IndexMap;
|
||||||
use log::{debug, error, info, trace, warn};
|
use log::{debug, info, trace, warn};
|
||||||
use serde::{Deserialize, Serialize};
|
use serde::{Deserialize, Serialize};
|
||||||
use serde_json::Value as JsonValue;
|
|
||||||
use serde_yaml::Value;
|
use serde_yaml::Value;
|
||||||
use typed_builder::TypedBuilder;
|
use typed_builder::TypedBuilder;
|
||||||
|
|
||||||
use crate::{
|
use crate::{ImageInspection, Module, ModuleExt};
|
||||||
akmods_info::AkmodsInfo,
|
|
||||||
constants::*,
|
|
||||||
ops::{self, check_command_exists},
|
|
||||||
};
|
|
||||||
|
|
||||||
#[derive(Default, Serialize, Clone, Deserialize, Debug, TypedBuilder)]
|
#[derive(Default, Serialize, Clone, Deserialize, Debug, TypedBuilder)]
|
||||||
pub struct Recipe<'a> {
|
pub struct Recipe<'a> {
|
||||||
|
|
@ -146,8 +136,8 @@ impl<'a> Recipe<'a> {
|
||||||
|
|
||||||
debug!("Recipe contents: {file}");
|
debug!("Recipe contents: {file}");
|
||||||
|
|
||||||
let mut recipe =
|
let mut recipe = serde_yaml::from_str::<Recipe>(&file)
|
||||||
serde_yaml::from_str::<Recipe>(&file).map_err(ops::serde_yaml_err(&file))?;
|
.map_err(blue_build_utils::serde_yaml_err(&file))?;
|
||||||
|
|
||||||
recipe.modules_ext.modules = Module::get_modules(&recipe.modules_ext.modules).into();
|
recipe.modules_ext.modules = Module::get_modules(&recipe.modules_ext.modules).into();
|
||||||
|
|
||||||
|
|
@ -157,7 +147,7 @@ impl<'a> Recipe<'a> {
|
||||||
pub fn get_os_version(&self) -> String {
|
pub fn get_os_version(&self) -> String {
|
||||||
trace!("Recipe::get_os_version()");
|
trace!("Recipe::get_os_version()");
|
||||||
|
|
||||||
if check_command_exists("skopeo").is_err() {
|
if blue_build_utils::check_command_exists("skopeo").is_err() {
|
||||||
warn!("The 'skopeo' command doesn't exist, falling back to version defined in recipe");
|
warn!("The 'skopeo' command doesn't exist, falling back to version defined in recipe");
|
||||||
return self.image_version.to_string();
|
return self.image_version.to_string();
|
||||||
}
|
}
|
||||||
|
|
@ -205,193 +195,3 @@ impl<'a> Recipe<'a> {
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
#[derive(Default, Serialize, Clone, Deserialize, Debug, TypedBuilder)]
|
|
||||||
pub struct ModuleExt<'a> {
|
|
||||||
#[builder(default, setter(into))]
|
|
||||||
pub modules: Cow<'a, [Module<'a>]>,
|
|
||||||
}
|
|
||||||
|
|
||||||
impl ModuleExt<'_> {
|
|
||||||
/// # Parse a module file returning a [`ModuleExt`]
|
|
||||||
///
|
|
||||||
/// # Errors
|
|
||||||
/// Can return an `anyhow` Error if the file cannot be read or deserialized
|
|
||||||
/// into a [`ModuleExt`]
|
|
||||||
pub fn parse_module_from_file(file_name: &str) -> Result<Self> {
|
|
||||||
let file_path = PathBuf::from("config").join(file_name);
|
|
||||||
let file_path = if file_path.is_absolute() {
|
|
||||||
file_path
|
|
||||||
} else {
|
|
||||||
std::env::current_dir()?.join(file_path)
|
|
||||||
};
|
|
||||||
|
|
||||||
let file = fs::read_to_string(file_path)?;
|
|
||||||
|
|
||||||
serde_yaml::from_str::<Self>(&file).map_or_else(
|
|
||||||
|_| -> Result<Self> {
|
|
||||||
let module =
|
|
||||||
serde_yaml::from_str::<Module>(&file).map_err(ops::serde_yaml_err(&file))?;
|
|
||||||
Ok(Self::builder().modules(vec![module]).build())
|
|
||||||
},
|
|
||||||
Ok,
|
|
||||||
)
|
|
||||||
}
|
|
||||||
|
|
||||||
pub fn get_akmods_info_list(&self, os_version: &str) -> Vec<AkmodsInfo> {
|
|
||||||
trace!("get_akmods_image_list({self:#?}, {os_version})");
|
|
||||||
|
|
||||||
let mut seen = HashSet::new();
|
|
||||||
|
|
||||||
self.modules
|
|
||||||
.iter()
|
|
||||||
.filter(|module| module.module_type.as_ref().is_some_and(|t| t == "akmods"))
|
|
||||||
.map(|module| module.generate_akmods_info(os_version))
|
|
||||||
.filter(|image| seen.insert(image.clone()))
|
|
||||||
.collect()
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
#[derive(Serialize, Deserialize, Debug, Clone, TypedBuilder)]
|
|
||||||
pub struct Module<'a> {
|
|
||||||
#[builder(default, setter(into, strip_option))]
|
|
||||||
#[serde(rename = "type", skip_serializing_if = "Option::is_none")]
|
|
||||||
pub module_type: Option<Cow<'a, str>>,
|
|
||||||
|
|
||||||
#[builder(default, setter(into, strip_option))]
|
|
||||||
#[serde(rename = "from-file", skip_serializing_if = "Option::is_none")]
|
|
||||||
pub from_file: Option<Cow<'a, str>>,
|
|
||||||
|
|
||||||
#[builder(default, setter(into, strip_option))]
|
|
||||||
#[serde(skip_serializing_if = "Option::is_none")]
|
|
||||||
pub source: Option<Cow<'a, str>>,
|
|
||||||
|
|
||||||
#[serde(flatten)]
|
|
||||||
#[builder(default, setter(into))]
|
|
||||||
pub config: IndexMap<String, Value>,
|
|
||||||
}
|
|
||||||
|
|
||||||
impl<'a> Module<'a> {
|
|
||||||
#[must_use]
|
|
||||||
pub fn get_modules(modules: &[Self]) -> Vec<Self> {
|
|
||||||
modules
|
|
||||||
.iter()
|
|
||||||
.flat_map(|module| {
|
|
||||||
module.from_file.as_ref().map_or_else(
|
|
||||||
|| vec![module.clone()],
|
|
||||||
|file_name| match ModuleExt::parse_module_from_file(file_name) {
|
|
||||||
Err(e) => {
|
|
||||||
error!("Failed to get module from {file_name}: {e}");
|
|
||||||
vec![]
|
|
||||||
}
|
|
||||||
Ok(module_ext) => Self::get_modules(&module_ext.modules),
|
|
||||||
},
|
|
||||||
)
|
|
||||||
})
|
|
||||||
.collect()
|
|
||||||
}
|
|
||||||
|
|
||||||
#[must_use]
|
|
||||||
pub fn get_module_type_list(&'a self, typ: &str, list_key: &str) -> Option<Vec<String>> {
|
|
||||||
if self.module_type.as_ref()? == typ {
|
|
||||||
Some(
|
|
||||||
self.config
|
|
||||||
.get(list_key)?
|
|
||||||
.as_sequence()?
|
|
||||||
.iter()
|
|
||||||
.filter_map(|t| Some(t.as_str()?.to_owned()))
|
|
||||||
.collect(),
|
|
||||||
)
|
|
||||||
} else {
|
|
||||||
None
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
#[must_use]
|
|
||||||
pub fn get_containerfile_list(&'a self) -> Option<Vec<String>> {
|
|
||||||
self.get_module_type_list("containerfile", "containerfiles")
|
|
||||||
}
|
|
||||||
|
|
||||||
#[must_use]
|
|
||||||
pub fn get_containerfile_snippets(&'a self) -> Option<Vec<String>> {
|
|
||||||
self.get_module_type_list("containerfile", "snippets")
|
|
||||||
}
|
|
||||||
|
|
||||||
pub fn print_module_context(&'a self) -> String {
|
|
||||||
serde_json::to_string(self).unwrap_or_else(|e| {
|
|
||||||
error!("Failed to parse module!!!!!: {e}");
|
|
||||||
process::exit(1);
|
|
||||||
})
|
|
||||||
}
|
|
||||||
|
|
||||||
pub fn get_files_list(&'a self) -> Option<Vec<(String, String)>> {
|
|
||||||
Some(
|
|
||||||
self.config
|
|
||||||
.get("files")?
|
|
||||||
.as_sequence()?
|
|
||||||
.iter()
|
|
||||||
.filter_map(|entry| entry.as_mapping())
|
|
||||||
.flatten()
|
|
||||||
.filter_map(|(src, dest)| {
|
|
||||||
Some((
|
|
||||||
format!("./config/files/{}", src.as_str()?),
|
|
||||||
dest.as_str()?.to_string(),
|
|
||||||
))
|
|
||||||
})
|
|
||||||
.collect(),
|
|
||||||
)
|
|
||||||
}
|
|
||||||
|
|
||||||
pub fn generate_akmods_info(&'a self, os_version: &str) -> AkmodsInfo {
|
|
||||||
trace!("generate_akmods_base({self:#?}, {os_version})");
|
|
||||||
|
|
||||||
let base = self
|
|
||||||
.config
|
|
||||||
.get("base")
|
|
||||||
.map(|b| b.as_str().unwrap_or_default());
|
|
||||||
let nvidia_version = self
|
|
||||||
.config
|
|
||||||
.get("nvidia-version")
|
|
||||||
.map(|v| v.as_u64().unwrap_or_default());
|
|
||||||
|
|
||||||
AkmodsInfo::builder()
|
|
||||||
.images(match (base, nvidia_version) {
|
|
||||||
(Some(b), Some(nv)) if !b.is_empty() && nv > 0 => (
|
|
||||||
format!("akmods:{b}-{os_version}"),
|
|
||||||
Some(format!("akmods-nvidia:{b}-{os_version}-{nv}")),
|
|
||||||
),
|
|
||||||
(Some(b), _) if !b.is_empty() => (format!("akmods:{b}-{os_version}"), None),
|
|
||||||
(_, Some(nv)) if nv > 0 => (
|
|
||||||
format!("akmods:main-{os_version}"),
|
|
||||||
Some(format!("akmods-nvidia:main-{os_version}")),
|
|
||||||
),
|
|
||||||
_ => (format!("akmods:main-{os_version}"), None),
|
|
||||||
})
|
|
||||||
.stage_name(format!(
|
|
||||||
"{}{}",
|
|
||||||
base.unwrap_or("main"),
|
|
||||||
nvidia_version.map_or_else(String::default, |nv| format!("-{nv}"))
|
|
||||||
))
|
|
||||||
.build()
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
#[derive(Deserialize, Debug, Clone)]
|
|
||||||
struct ImageInspection {
|
|
||||||
#[serde(alias = "Labels")]
|
|
||||||
labels: HashMap<String, JsonValue>,
|
|
||||||
}
|
|
||||||
|
|
||||||
impl ImageInspection {
|
|
||||||
pub fn get_version(&self) -> Option<String> {
|
|
||||||
Some(
|
|
||||||
self.labels
|
|
||||||
.get("org.opencontainers.image.version")?
|
|
||||||
.as_str()
|
|
||||||
.map(std::string::ToString::to_string)?
|
|
||||||
.split('.')
|
|
||||||
.take(1)
|
|
||||||
.collect(),
|
|
||||||
)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
@ -3,6 +3,8 @@ use log::error;
|
||||||
use clap::{command, crate_authors, Parser, Subcommand};
|
use clap::{command, crate_authors, Parser, Subcommand};
|
||||||
use clap_verbosity_flag::{InfoLevel, Verbosity};
|
use clap_verbosity_flag::{InfoLevel, Verbosity};
|
||||||
|
|
||||||
|
use crate::shadow;
|
||||||
|
|
||||||
pub mod bug_report;
|
pub mod bug_report;
|
||||||
pub mod build;
|
pub mod build;
|
||||||
pub mod completions;
|
pub mod completions;
|
||||||
|
|
@ -10,7 +12,6 @@ pub mod completions;
|
||||||
pub mod init;
|
pub mod init;
|
||||||
pub mod local;
|
pub mod local;
|
||||||
pub mod template;
|
pub mod template;
|
||||||
pub mod utils;
|
|
||||||
|
|
||||||
pub trait BlueBuildCommand {
|
pub trait BlueBuildCommand {
|
||||||
/// Runs the command and returns a result
|
/// Runs the command and returns a result
|
||||||
|
|
@ -29,8 +30,6 @@ pub trait BlueBuildCommand {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
shadow_rs::shadow!(shadow);
|
|
||||||
|
|
||||||
#[derive(Parser, Debug)]
|
#[derive(Parser, Debug)]
|
||||||
#[clap(
|
#[clap(
|
||||||
name = "BlueBuild",
|
name = "BlueBuild",
|
||||||
|
|
|
||||||
|
|
@ -1,17 +1,17 @@
|
||||||
use askama::Template;
|
use blue_build_recipe::Recipe;
|
||||||
|
use blue_build_template::{GithubIssueTemplate, Template};
|
||||||
|
use blue_build_utils::constants::*;
|
||||||
use clap::Args;
|
use clap::Args;
|
||||||
use clap_complete::Shell;
|
use clap_complete::Shell;
|
||||||
use fuzzy_matcher::{skim::SkimMatcherV2, FuzzyMatcher};
|
use fuzzy_matcher::{skim::SkimMatcherV2, FuzzyMatcher};
|
||||||
use log::{debug, error, trace};
|
use log::{debug, error, trace};
|
||||||
use requestty::question::{completions, Completions};
|
use requestty::question::{completions, Completions};
|
||||||
use std::borrow::Cow;
|
|
||||||
use std::time::Duration;
|
use std::time::Duration;
|
||||||
use typed_builder::TypedBuilder;
|
use typed_builder::TypedBuilder;
|
||||||
|
|
||||||
use super::utils::exec_cmd;
|
|
||||||
use super::BlueBuildCommand;
|
use super::BlueBuildCommand;
|
||||||
|
|
||||||
use crate::{constants::*, module_recipe::Recipe, shadow};
|
use crate::shadow;
|
||||||
|
|
||||||
#[derive(Default, Debug, Clone, TypedBuilder, Args)]
|
#[derive(Default, Debug, Clone, TypedBuilder, Args)]
|
||||||
pub struct BugReportRecipe {
|
pub struct BugReportRecipe {
|
||||||
|
|
@ -272,7 +272,7 @@ fn get_shell_version(shell: &str) -> String {
|
||||||
error!("Powershell is not supported.");
|
error!("Powershell is not supported.");
|
||||||
None
|
None
|
||||||
}
|
}
|
||||||
_ => exec_cmd(shell, &["--version"], time_limit),
|
_ => blue_build_utils::exec_cmd(shell, &["--version"], time_limit),
|
||||||
}
|
}
|
||||||
.map_or_else(
|
.map_or_else(
|
||||||
|| UNKNOWN_VERSION.to_string(),
|
|| UNKNOWN_VERSION.to_string(),
|
||||||
|
|
@ -284,52 +284,6 @@ fn get_shell_version(shell: &str) -> String {
|
||||||
// Git
|
// Git
|
||||||
// ============================================================================= //
|
// ============================================================================= //
|
||||||
|
|
||||||
#[derive(Debug, Clone, Template, TypedBuilder)]
|
|
||||||
#[template(path = "github_issue.j2", escape = "md")]
|
|
||||||
struct GithubIssueTemplate<'a> {
|
|
||||||
#[builder(setter(into))]
|
|
||||||
bb_version: Cow<'a, str>,
|
|
||||||
|
|
||||||
#[builder(setter(into))]
|
|
||||||
build_rust_channel: Cow<'a, str>,
|
|
||||||
|
|
||||||
#[builder(setter(into))]
|
|
||||||
build_time: Cow<'a, str>,
|
|
||||||
|
|
||||||
#[builder(setter(into))]
|
|
||||||
git_commit_hash: Cow<'a, str>,
|
|
||||||
|
|
||||||
#[builder(setter(into))]
|
|
||||||
os_name: Cow<'a, str>,
|
|
||||||
|
|
||||||
#[builder(setter(into))]
|
|
||||||
os_version: Cow<'a, str>,
|
|
||||||
|
|
||||||
#[builder(setter(into))]
|
|
||||||
pkg_branch_tag: Cow<'a, str>,
|
|
||||||
|
|
||||||
#[builder(setter(into))]
|
|
||||||
recipe: Cow<'a, str>,
|
|
||||||
|
|
||||||
#[builder(setter(into))]
|
|
||||||
rust_channel: Cow<'a, str>,
|
|
||||||
|
|
||||||
#[builder(setter(into))]
|
|
||||||
rust_version: Cow<'a, str>,
|
|
||||||
|
|
||||||
#[builder(setter(into))]
|
|
||||||
shell_name: Cow<'a, str>,
|
|
||||||
|
|
||||||
#[builder(setter(into))]
|
|
||||||
shell_version: Cow<'a, str>,
|
|
||||||
|
|
||||||
#[builder(setter(into))]
|
|
||||||
terminal_name: Cow<'a, str>,
|
|
||||||
|
|
||||||
#[builder(setter(into))]
|
|
||||||
terminal_version: Cow<'a, str>,
|
|
||||||
}
|
|
||||||
|
|
||||||
fn get_pkg_branch_tag() -> String {
|
fn get_pkg_branch_tag() -> String {
|
||||||
format!("{} ({})", shadow::BRANCH, shadow::LAST_TAG)
|
format!("{} ({})", shadow::BRANCH, shadow::LAST_TAG)
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -8,6 +8,8 @@ use std::{
|
||||||
};
|
};
|
||||||
|
|
||||||
use anyhow::{anyhow, bail, Result};
|
use anyhow::{anyhow, bail, Result};
|
||||||
|
use blue_build_recipe::Recipe;
|
||||||
|
use blue_build_utils::constants::*;
|
||||||
use clap::Args;
|
use clap::Args;
|
||||||
use colorized::{Color, Colors};
|
use colorized::{Color, Colors};
|
||||||
use log::{debug, info, trace, warn};
|
use log::{debug, info, trace, warn};
|
||||||
|
|
@ -35,12 +37,7 @@ use tokio::{
|
||||||
sync::oneshot::{self, Sender},
|
sync::oneshot::{self, Sender},
|
||||||
};
|
};
|
||||||
|
|
||||||
use crate::{
|
use crate::commands::template::TemplateCommand;
|
||||||
commands::template::TemplateCommand,
|
|
||||||
constants::{self, *},
|
|
||||||
module_recipe::Recipe,
|
|
||||||
ops,
|
|
||||||
};
|
|
||||||
|
|
||||||
use super::BlueBuildCommand;
|
use super::BlueBuildCommand;
|
||||||
|
|
||||||
|
|
@ -181,19 +178,19 @@ impl BlueBuildCommand for BuildCommand {
|
||||||
// -> If it does => *Ask* to add to .gitignore and remove from git
|
// -> If it does => *Ask* to add to .gitignore and remove from git
|
||||||
// -> If it doesn't => *Ask* to continue and override the file
|
// -> If it doesn't => *Ask* to continue and override the file
|
||||||
|
|
||||||
let container_file_path = Path::new(constants::CONTAINER_FILE);
|
let container_file_path = Path::new(CONTAINER_FILE);
|
||||||
|
|
||||||
if !self.force && container_file_path.exists() {
|
if !self.force && container_file_path.exists() {
|
||||||
let gitignore = fs::read_to_string(constants::GITIGNORE_PATH)?;
|
let gitignore = fs::read_to_string(GITIGNORE_PATH)?;
|
||||||
|
|
||||||
let is_ignored = gitignore
|
let is_ignored = gitignore
|
||||||
.lines()
|
.lines()
|
||||||
.any(|line: &str| line.contains(constants::CONTAINER_FILE));
|
.any(|line: &str| line.contains(CONTAINER_FILE));
|
||||||
|
|
||||||
if !is_ignored {
|
if !is_ignored {
|
||||||
let containerfile = fs::read_to_string(container_file_path)?;
|
let containerfile = fs::read_to_string(container_file_path)?;
|
||||||
let has_label = containerfile.lines().any(|line| {
|
let has_label = containerfile.lines().any(|line| {
|
||||||
let label = format!("LABEL {}", constants::BUILD_ID_LABEL);
|
let label = format!("LABEL {}", BUILD_ID_LABEL);
|
||||||
line.to_string().trim().starts_with(&label)
|
line.to_string().trim().starts_with(&label)
|
||||||
});
|
});
|
||||||
|
|
||||||
|
|
@ -211,9 +208,9 @@ impl BlueBuildCommand for BuildCommand {
|
||||||
|
|
||||||
if let Ok(answer) = requestty::prompt_one(question) {
|
if let Ok(answer) = requestty::prompt_one(question) {
|
||||||
if answer.as_bool().unwrap_or(false) {
|
if answer.as_bool().unwrap_or(false) {
|
||||||
ops::append_to_file(
|
blue_build_utils::append_to_file(
|
||||||
constants::GITIGNORE_PATH,
|
GITIGNORE_PATH,
|
||||||
&format!("/{}", constants::CONTAINER_FILE),
|
&format!("/{}", CONTAINER_FILE),
|
||||||
)?;
|
)?;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
@ -231,15 +228,15 @@ impl BlueBuildCommand for BuildCommand {
|
||||||
.unwrap_or_else(|| PathBuf::from(RECIPE_PATH));
|
.unwrap_or_else(|| PathBuf::from(RECIPE_PATH));
|
||||||
|
|
||||||
#[cfg(not(feature = "podman-api"))]
|
#[cfg(not(feature = "podman-api"))]
|
||||||
if let Err(e1) = ops::check_command_exists("buildah") {
|
if let Err(e1) = blue_build_utils::check_command_exists("buildah") {
|
||||||
ops::check_command_exists("podman").map_err(|e2| {
|
blue_build_utils::check_command_exists("podman").map_err(|e2| {
|
||||||
anyhow!("Need either 'buildah' or 'podman' commands to proceed: {e1}, {e2}")
|
anyhow!("Need either 'buildah' or 'podman' commands to proceed: {e1}, {e2}")
|
||||||
})?;
|
})?;
|
||||||
}
|
}
|
||||||
|
|
||||||
if self.push {
|
if self.push {
|
||||||
ops::check_command_exists("cosign")?;
|
blue_build_utils::check_command_exists("cosign")?;
|
||||||
ops::check_command_exists("skopeo")?;
|
blue_build_utils::check_command_exists("skopeo")?;
|
||||||
check_cosign_files()?;
|
check_cosign_files()?;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -420,8 +417,8 @@ impl BuildCommand {
|
||||||
|
|
||||||
info!("Logging into the registry, {registry}");
|
info!("Logging into the registry, {registry}");
|
||||||
let login_output = match (
|
let login_output = match (
|
||||||
ops::check_command_exists("buildah"),
|
blue_build_utils::check_command_exists("buildah"),
|
||||||
ops::check_command_exists("podman"),
|
blue_build_utils::check_command_exists("podman"),
|
||||||
) {
|
) {
|
||||||
(Ok(()), _) => {
|
(Ok(()), _) => {
|
||||||
trace!("buildah login -u {username} -p [MASKED] {registry}");
|
trace!("buildah login -u {username} -p [MASKED] {registry}");
|
||||||
|
|
@ -550,8 +547,8 @@ impl BuildCommand {
|
||||||
|
|
||||||
info!("Building image {full_image}");
|
info!("Building image {full_image}");
|
||||||
let status = match (
|
let status = match (
|
||||||
ops::check_command_exists("buildah"),
|
blue_build_utils::check_command_exists("buildah"),
|
||||||
ops::check_command_exists("podman"),
|
blue_build_utils::check_command_exists("podman"),
|
||||||
) {
|
) {
|
||||||
(Ok(()), _) => {
|
(Ok(()), _) => {
|
||||||
trace!("buildah build -t {full_image}");
|
trace!("buildah build -t {full_image}");
|
||||||
|
|
@ -588,7 +585,7 @@ impl BuildCommand {
|
||||||
let retry_count = if retry { self.retry_count } else { 0 };
|
let retry_count = if retry { self.retry_count } else { 0 };
|
||||||
|
|
||||||
// Push images with retries (1s delay between retries)
|
// Push images with retries (1s delay between retries)
|
||||||
ops::retry(retry_count, 1000, || push_images(tags, image_name))?;
|
blue_build_utils::retry(retry_count, 1000, || push_images(tags, image_name))?;
|
||||||
sign_images(image_name, tags.first().map(String::as_str))?;
|
sign_images(image_name, tags.first().map(String::as_str))?;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -963,8 +960,8 @@ fn tag_images(tags: &[String], image_name: &str, full_image: &str) -> Result<()>
|
||||||
let tag_image = format!("{image_name}:{tag}");
|
let tag_image = format!("{image_name}:{tag}");
|
||||||
|
|
||||||
let status = match (
|
let status = match (
|
||||||
ops::check_command_exists("buildah"),
|
blue_build_utils::check_command_exists("buildah"),
|
||||||
ops::check_command_exists("podman"),
|
blue_build_utils::check_command_exists("podman"),
|
||||||
) {
|
) {
|
||||||
(Ok(()), _) => {
|
(Ok(()), _) => {
|
||||||
trace!("buildah tag {full_image} {tag_image}");
|
trace!("buildah tag {full_image} {tag_image}");
|
||||||
|
|
@ -1001,8 +998,8 @@ fn push_images(tags: &[String], image_name: &str) -> Result<()> {
|
||||||
let tag_image = format!("{image_name}:{tag}");
|
let tag_image = format!("{image_name}:{tag}");
|
||||||
|
|
||||||
let status = match (
|
let status = match (
|
||||||
ops::check_command_exists("buildah"),
|
blue_build_utils::check_command_exists("buildah"),
|
||||||
ops::check_command_exists("podman"),
|
blue_build_utils::check_command_exists("podman"),
|
||||||
) {
|
) {
|
||||||
(Ok(()), _) => {
|
(Ok(()), _) => {
|
||||||
trace!("buildah push {tag_image}");
|
trace!("buildah push {tag_image}");
|
||||||
|
|
|
||||||
|
|
@ -4,10 +4,9 @@ use std::{
|
||||||
};
|
};
|
||||||
|
|
||||||
use anyhow::{bail, Result};
|
use anyhow::{bail, Result};
|
||||||
|
use blue_build_utils::constants::*;
|
||||||
use log::trace;
|
use log::trace;
|
||||||
|
|
||||||
use crate::{constants::*, ops};
|
|
||||||
|
|
||||||
#[cfg(feature = "podman-api")]
|
#[cfg(feature = "podman-api")]
|
||||||
#[derive(Debug, Clone, Default)]
|
#[derive(Debug, Clone, Default)]
|
||||||
pub enum BuildStrategy {
|
pub enum BuildStrategy {
|
||||||
|
|
@ -29,8 +28,8 @@ impl BuildStrategy {
|
||||||
PathBuf::from(RUN_PODMAN_SOCK),
|
PathBuf::from(RUN_PODMAN_SOCK),
|
||||||
PathBuf::from(VAR_RUN_PODMAN_PODMAN_SOCK),
|
PathBuf::from(VAR_RUN_PODMAN_PODMAN_SOCK),
|
||||||
PathBuf::from(VAR_RUN_PODMAN_SOCK),
|
PathBuf::from(VAR_RUN_PODMAN_SOCK),
|
||||||
ops::check_command_exists("buildah"),
|
blue_build_utils::check_command_exists("buildah"),
|
||||||
ops::check_command_exists("podman"),
|
blue_build_utils::check_command_exists("podman"),
|
||||||
) {
|
) {
|
||||||
(Ok(xdg_runtime), _, _, _, _, _)
|
(Ok(xdg_runtime), _, _, _, _, _)
|
||||||
if Path::new(&format!("{xdg_runtime}/podman/podman.sock")).exists() =>
|
if Path::new(&format!("{xdg_runtime}/podman/podman.sock")).exists() =>
|
||||||
|
|
|
||||||
|
|
@ -5,17 +5,14 @@ use std::{
|
||||||
};
|
};
|
||||||
|
|
||||||
use anyhow::{bail, Result};
|
use anyhow::{bail, Result};
|
||||||
|
use blue_build_recipe::Recipe;
|
||||||
|
use blue_build_utils::constants::*;
|
||||||
use clap::Args;
|
use clap::Args;
|
||||||
use log::{debug, info, trace};
|
use log::{debug, info, trace};
|
||||||
use typed_builder::TypedBuilder;
|
use typed_builder::TypedBuilder;
|
||||||
use users::{Users, UsersCache};
|
use users::{Users, UsersCache};
|
||||||
|
|
||||||
use crate::{
|
use crate::commands::build::BuildCommand;
|
||||||
commands::build::BuildCommand,
|
|
||||||
constants::{ARCHIVE_SUFFIX, LOCAL_BUILD},
|
|
||||||
module_recipe::Recipe,
|
|
||||||
ops,
|
|
||||||
};
|
|
||||||
|
|
||||||
use super::BlueBuildCommand;
|
use super::BlueBuildCommand;
|
||||||
|
|
||||||
|
|
@ -148,7 +145,7 @@ impl BlueBuildCommand for RebaseCommand {
|
||||||
fn check_can_run() -> Result<()> {
|
fn check_can_run() -> Result<()> {
|
||||||
trace!("check_can_run()");
|
trace!("check_can_run()");
|
||||||
|
|
||||||
ops::check_command_exists("rpm-ostree")?;
|
blue_build_utils::check_command_exists("rpm-ostree")?;
|
||||||
|
|
||||||
let cache = UsersCache::new();
|
let cache = UsersCache::new();
|
||||||
if cache.get_current_uid() != 0 {
|
if cache.get_current_uid() != 0 {
|
||||||
|
|
|
||||||
|
|
@ -1,57 +1,16 @@
|
||||||
use std::{
|
use std::path::PathBuf;
|
||||||
env, fs,
|
|
||||||
path::{Path, PathBuf},
|
|
||||||
process,
|
|
||||||
};
|
|
||||||
|
|
||||||
use anyhow::{anyhow, Result};
|
use anyhow::{anyhow, Result};
|
||||||
use askama::Template;
|
use blue_build_recipe::Recipe;
|
||||||
|
use blue_build_template::{ContainerFileTemplate, Template};
|
||||||
|
use blue_build_utils::constants::*;
|
||||||
use clap::Args;
|
use clap::Args;
|
||||||
use log::{debug, error, info, trace};
|
use log::{debug, info, trace};
|
||||||
use typed_builder::TypedBuilder;
|
use typed_builder::TypedBuilder;
|
||||||
use uuid::Uuid;
|
use uuid::Uuid;
|
||||||
|
|
||||||
use crate::{constants::*, module_recipe::Recipe};
|
|
||||||
|
|
||||||
use super::BlueBuildCommand;
|
use super::BlueBuildCommand;
|
||||||
|
|
||||||
#[derive(Debug, Clone, Template, TypedBuilder)]
|
|
||||||
#[template(path = "Containerfile.j2", escape = "none")]
|
|
||||||
pub struct ContainerFileTemplate<'a> {
|
|
||||||
recipe: &'a Recipe<'a>,
|
|
||||||
|
|
||||||
#[builder(setter(into))]
|
|
||||||
recipe_path: &'a Path,
|
|
||||||
|
|
||||||
#[builder(setter(into))]
|
|
||||||
build_id: Uuid,
|
|
||||||
|
|
||||||
#[builder(default)]
|
|
||||||
export_script: ExportsTemplate,
|
|
||||||
}
|
|
||||||
|
|
||||||
#[derive(Debug, Clone, Default, Template)]
|
|
||||||
#[template(path = "export.sh", escape = "none")]
|
|
||||||
pub struct ExportsTemplate;
|
|
||||||
|
|
||||||
impl ExportsTemplate {
|
|
||||||
fn print_script(&self) -> String {
|
|
||||||
trace!("print_script({self})");
|
|
||||||
|
|
||||||
format!(
|
|
||||||
"\"{}\"",
|
|
||||||
self.render()
|
|
||||||
.unwrap_or_else(|e| {
|
|
||||||
error!("Failed to render export.sh script: {e}");
|
|
||||||
process::exit(1);
|
|
||||||
})
|
|
||||||
.replace('\n', "\\n")
|
|
||||||
.replace('\"', "\\\"")
|
|
||||||
.replace('$', "\\$")
|
|
||||||
)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
#[derive(Debug, Clone, Args, TypedBuilder)]
|
#[derive(Debug, Clone, Args, TypedBuilder)]
|
||||||
pub struct TemplateCommand {
|
pub struct TemplateCommand {
|
||||||
/// The recipe file to create a template from
|
/// The recipe file to create a template from
|
||||||
|
|
@ -127,48 +86,3 @@ impl TemplateCommand {
|
||||||
// ======================================================== //
|
// ======================================================== //
|
||||||
// ========================= Helpers ====================== //
|
// ========================= Helpers ====================== //
|
||||||
// ======================================================== //
|
// ======================================================== //
|
||||||
|
|
||||||
fn has_cosign_file() -> bool {
|
|
||||||
trace!("has_cosign_file()");
|
|
||||||
std::env::current_dir()
|
|
||||||
.map(|p| p.join(COSIGN_PATH).exists())
|
|
||||||
.unwrap_or(false)
|
|
||||||
}
|
|
||||||
|
|
||||||
#[must_use]
|
|
||||||
fn print_containerfile(containerfile: &str) -> String {
|
|
||||||
trace!("print_containerfile({containerfile})");
|
|
||||||
debug!("Loading containerfile contents for {containerfile}");
|
|
||||||
|
|
||||||
let path = format!("config/containerfiles/{containerfile}/Containerfile");
|
|
||||||
|
|
||||||
let file = fs::read_to_string(&path).unwrap_or_else(|e| {
|
|
||||||
error!("Failed to read file {path}: {e}");
|
|
||||||
process::exit(1);
|
|
||||||
});
|
|
||||||
|
|
||||||
debug!("Containerfile contents {path}:\n{file}");
|
|
||||||
|
|
||||||
file
|
|
||||||
}
|
|
||||||
|
|
||||||
fn get_github_repo_owner() -> Option<String> {
|
|
||||||
Some(env::var(GITHUB_REPOSITORY_OWNER).ok()?.to_lowercase())
|
|
||||||
}
|
|
||||||
|
|
||||||
fn get_gitlab_registry_path() -> Option<String> {
|
|
||||||
Some(
|
|
||||||
format!(
|
|
||||||
"{}/{}/{}",
|
|
||||||
env::var(CI_REGISTRY).ok()?,
|
|
||||||
env::var(CI_PROJECT_NAMESPACE).ok()?,
|
|
||||||
env::var(CI_PROJECT_NAME).ok()?,
|
|
||||||
)
|
|
||||||
.to_lowercase(),
|
|
||||||
)
|
|
||||||
}
|
|
||||||
|
|
||||||
fn modules_exists() -> bool {
|
|
||||||
let mod_path = Path::new("modules");
|
|
||||||
mod_path.exists() && mod_path.is_dir()
|
|
||||||
}
|
|
||||||
|
|
|
||||||
13
src/lib.rs
13
src/lib.rs
|
|
@ -1,19 +1,6 @@
|
||||||
//! The root library for blue-build.
|
//! The root library for blue-build.
|
||||||
#![warn(
|
|
||||||
clippy::correctness,
|
|
||||||
clippy::suspicious,
|
|
||||||
clippy::perf,
|
|
||||||
clippy::style,
|
|
||||||
clippy::nursery
|
|
||||||
)]
|
|
||||||
#![doc = include_str!("../README.md")]
|
#![doc = include_str!("../README.md")]
|
||||||
#![forbid(unsafe_code)]
|
|
||||||
#![allow(clippy::module_name_repetitions)]
|
|
||||||
|
|
||||||
shadow_rs::shadow!(shadow);
|
shadow_rs::shadow!(shadow);
|
||||||
|
|
||||||
pub mod akmods_info;
|
|
||||||
pub mod commands;
|
pub mod commands;
|
||||||
pub mod constants;
|
|
||||||
pub mod module_recipe;
|
|
||||||
mod ops;
|
|
||||||
|
|
|
||||||
25
template/Cargo.toml
Normal file
25
template/Cargo.toml
Normal file
|
|
@ -0,0 +1,25 @@
|
||||||
|
[package]
|
||||||
|
name = "blue-build-template"
|
||||||
|
version.workspace = true
|
||||||
|
edition.workspace = true
|
||||||
|
description.workspace = true
|
||||||
|
repository.workspace = true
|
||||||
|
license.workspace = true
|
||||||
|
|
||||||
|
# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html
|
||||||
|
|
||||||
|
[dependencies]
|
||||||
|
askama = { version = "0.12", features = ["serde-json", "serde-yaml"] }
|
||||||
|
blue-build-recipe = { path = "../recipe" }
|
||||||
|
blue-build-utils = { path = "../utils" }
|
||||||
|
|
||||||
|
log.workspace = true
|
||||||
|
serde.workspace = true
|
||||||
|
serde_yaml.workspace = true
|
||||||
|
serde_json.workspace = true
|
||||||
|
typed-builder.workspace = true
|
||||||
|
uuid.workspace = true
|
||||||
|
|
||||||
|
[lints]
|
||||||
|
workspace = true
|
||||||
|
|
||||||
137
template/src/lib.rs
Normal file
137
template/src/lib.rs
Normal file
|
|
@ -0,0 +1,137 @@
|
||||||
|
use std::{borrow::Cow, env, fs, path::Path, process};
|
||||||
|
|
||||||
|
use blue_build_recipe::Recipe;
|
||||||
|
use blue_build_utils::constants::*;
|
||||||
|
use log::{debug, error, trace};
|
||||||
|
use typed_builder::TypedBuilder;
|
||||||
|
use uuid::Uuid;
|
||||||
|
|
||||||
|
pub use askama::Template;
|
||||||
|
|
||||||
|
#[derive(Debug, Clone, Template, TypedBuilder)]
|
||||||
|
#[template(path = "Containerfile.j2", escape = "none")]
|
||||||
|
pub struct ContainerFileTemplate<'a> {
|
||||||
|
recipe: &'a Recipe<'a>,
|
||||||
|
|
||||||
|
#[builder(setter(into))]
|
||||||
|
recipe_path: &'a Path,
|
||||||
|
|
||||||
|
#[builder(setter(into))]
|
||||||
|
build_id: Uuid,
|
||||||
|
|
||||||
|
#[builder(default)]
|
||||||
|
export_script: ExportsTemplate,
|
||||||
|
}
|
||||||
|
|
||||||
|
#[derive(Debug, Clone, Default, Template)]
|
||||||
|
#[template(path = "export.sh", escape = "none")]
|
||||||
|
pub struct ExportsTemplate;
|
||||||
|
|
||||||
|
impl ExportsTemplate {
|
||||||
|
fn print_script(&self) -> String {
|
||||||
|
trace!("print_script({self})");
|
||||||
|
|
||||||
|
format!(
|
||||||
|
"\"{}\"",
|
||||||
|
self.render()
|
||||||
|
.unwrap_or_else(|e| {
|
||||||
|
error!("Failed to render export.sh script: {e}");
|
||||||
|
process::exit(1);
|
||||||
|
})
|
||||||
|
.replace('\n', "\\n")
|
||||||
|
.replace('\"', "\\\"")
|
||||||
|
.replace('$', "\\$")
|
||||||
|
)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#[derive(Debug, Clone, Template, TypedBuilder)]
|
||||||
|
#[template(path = "github_issue.j2", escape = "md")]
|
||||||
|
pub struct GithubIssueTemplate<'a> {
|
||||||
|
#[builder(setter(into))]
|
||||||
|
bb_version: Cow<'a, str>,
|
||||||
|
|
||||||
|
#[builder(setter(into))]
|
||||||
|
build_rust_channel: Cow<'a, str>,
|
||||||
|
|
||||||
|
#[builder(setter(into))]
|
||||||
|
build_time: Cow<'a, str>,
|
||||||
|
|
||||||
|
#[builder(setter(into))]
|
||||||
|
git_commit_hash: Cow<'a, str>,
|
||||||
|
|
||||||
|
#[builder(setter(into))]
|
||||||
|
os_name: Cow<'a, str>,
|
||||||
|
|
||||||
|
#[builder(setter(into))]
|
||||||
|
os_version: Cow<'a, str>,
|
||||||
|
|
||||||
|
#[builder(setter(into))]
|
||||||
|
pkg_branch_tag: Cow<'a, str>,
|
||||||
|
|
||||||
|
#[builder(setter(into))]
|
||||||
|
recipe: Cow<'a, str>,
|
||||||
|
|
||||||
|
#[builder(setter(into))]
|
||||||
|
rust_channel: Cow<'a, str>,
|
||||||
|
|
||||||
|
#[builder(setter(into))]
|
||||||
|
rust_version: Cow<'a, str>,
|
||||||
|
|
||||||
|
#[builder(setter(into))]
|
||||||
|
shell_name: Cow<'a, str>,
|
||||||
|
|
||||||
|
#[builder(setter(into))]
|
||||||
|
shell_version: Cow<'a, str>,
|
||||||
|
|
||||||
|
#[builder(setter(into))]
|
||||||
|
terminal_name: Cow<'a, str>,
|
||||||
|
|
||||||
|
#[builder(setter(into))]
|
||||||
|
terminal_version: Cow<'a, str>,
|
||||||
|
}
|
||||||
|
|
||||||
|
fn has_cosign_file() -> bool {
|
||||||
|
trace!("has_cosign_file()");
|
||||||
|
std::env::current_dir()
|
||||||
|
.map(|p| p.join(COSIGN_PATH).exists())
|
||||||
|
.unwrap_or(false)
|
||||||
|
}
|
||||||
|
|
||||||
|
#[must_use]
|
||||||
|
fn print_containerfile(containerfile: &str) -> String {
|
||||||
|
trace!("print_containerfile({containerfile})");
|
||||||
|
debug!("Loading containerfile contents for {containerfile}");
|
||||||
|
|
||||||
|
let path = format!("config/containerfiles/{containerfile}/Containerfile");
|
||||||
|
|
||||||
|
let file = fs::read_to_string(&path).unwrap_or_else(|e| {
|
||||||
|
error!("Failed to read file {path}: {e}");
|
||||||
|
process::exit(1);
|
||||||
|
});
|
||||||
|
|
||||||
|
debug!("Containerfile contents {path}:\n{file}");
|
||||||
|
|
||||||
|
file
|
||||||
|
}
|
||||||
|
|
||||||
|
fn get_github_repo_owner() -> Option<String> {
|
||||||
|
Some(env::var(GITHUB_REPOSITORY_OWNER).ok()?.to_lowercase())
|
||||||
|
}
|
||||||
|
|
||||||
|
fn get_gitlab_registry_path() -> Option<String> {
|
||||||
|
Some(
|
||||||
|
format!(
|
||||||
|
"{}/{}/{}",
|
||||||
|
env::var(CI_REGISTRY).ok()?,
|
||||||
|
env::var(CI_PROJECT_NAMESPACE).ok()?,
|
||||||
|
env::var(CI_PROJECT_NAME).ok()?,
|
||||||
|
)
|
||||||
|
.to_lowercase(),
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
fn modules_exists() -> bool {
|
||||||
|
let mod_path = Path::new("modules");
|
||||||
|
mod_path.exists() && mod_path.is_dir()
|
||||||
|
}
|
||||||
|
|
@ -23,7 +23,7 @@ RUN printf {{ export_script.print_script() }} >> /exports.sh && chmod +x /export
|
||||||
|
|
||||||
FROM {{ recipe.base_image }}:{{ recipe.image_version }}
|
FROM {{ recipe.base_image }}:{{ recipe.image_version }}
|
||||||
|
|
||||||
LABEL {{ crate::constants::BUILD_ID_LABEL }}="{{ build_id }}"
|
LABEL {{ blue_build_utils::constants::BUILD_ID_LABEL }}="{{ build_id }}"
|
||||||
LABEL org.opencontainers.image.title="{{ recipe.name }}"
|
LABEL org.opencontainers.image.title="{{ recipe.name }}"
|
||||||
LABEL org.opencontainers.image.description="{{ recipe.description }}"
|
LABEL org.opencontainers.image.description="{{ recipe.description }}"
|
||||||
LABEL io.artifacthub.package.readme-url=https://raw.githubusercontent.com/blue-build/cli/main/README.md
|
LABEL io.artifacthub.package.readme-url=https://raw.githubusercontent.com/blue-build/cli/main/README.md
|
||||||
25
utils/Cargo.toml
Normal file
25
utils/Cargo.toml
Normal file
|
|
@ -0,0 +1,25 @@
|
||||||
|
[package]
|
||||||
|
name = "blue-build-utils"
|
||||||
|
version.workspace = true
|
||||||
|
edition.workspace = true
|
||||||
|
description.workspace = true
|
||||||
|
repository.workspace = true
|
||||||
|
license.workspace = true
|
||||||
|
|
||||||
|
# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html
|
||||||
|
|
||||||
|
[dependencies]
|
||||||
|
directories = "5"
|
||||||
|
process_control = { version = "4.0.3", features = ["crossbeam-channel"] }
|
||||||
|
which = "6"
|
||||||
|
|
||||||
|
anyhow.workspace = true
|
||||||
|
format_serde_error.workspace = true
|
||||||
|
log.workspace = true
|
||||||
|
serde.workspace = true
|
||||||
|
serde_yaml.workspace = true
|
||||||
|
serde_json.workspace = true
|
||||||
|
|
||||||
|
[lints]
|
||||||
|
workspace = true
|
||||||
|
|
||||||
|
|
@ -1,19 +1,12 @@
|
||||||
|
use std::{
|
||||||
|
ffi::OsStr,
|
||||||
|
fmt::Debug,
|
||||||
|
io::{Error, ErrorKind, Result},
|
||||||
|
process::{Command, Stdio},
|
||||||
|
time::{Duration, Instant},
|
||||||
|
};
|
||||||
|
|
||||||
use process_control::{ChildExt, Control};
|
use process_control::{ChildExt, Control};
|
||||||
use std::ffi::OsStr;
|
|
||||||
use std::fmt::Debug;
|
|
||||||
use std::io::{Error, ErrorKind, Result};
|
|
||||||
use std::path::PathBuf;
|
|
||||||
use std::process::{Command, Stdio};
|
|
||||||
use std::time::{Duration, Instant};
|
|
||||||
|
|
||||||
#[must_use]
|
|
||||||
pub fn home_dir() -> Option<PathBuf> {
|
|
||||||
directories::BaseDirs::new().map(|base_dirs| base_dirs.home_dir().to_path_buf())
|
|
||||||
}
|
|
||||||
|
|
||||||
// ================================================================================================= //
|
|
||||||
// CommandOutput
|
|
||||||
// ================================================================================================= //
|
|
||||||
|
|
||||||
#[derive(Debug, Clone, PartialEq, Eq)]
|
#[derive(Debug, Clone, PartialEq, Eq)]
|
||||||
pub struct CommandOutput {
|
pub struct CommandOutput {
|
||||||
|
|
@ -27,7 +20,7 @@ pub struct CommandOutput {
|
||||||
/// #
|
/// #
|
||||||
/// # Errors
|
/// # Errors
|
||||||
///
|
///
|
||||||
pub fn create_command<T: AsRef<OsStr>>(binary_name: T) -> Result<Command> {
|
fn create_command<T: AsRef<OsStr>>(binary_name: T) -> Result<Command> {
|
||||||
let binary_name = binary_name.as_ref();
|
let binary_name = binary_name.as_ref();
|
||||||
log::trace!("Creating Command for binary {:?}", binary_name);
|
log::trace!("Creating Command for binary {:?}", binary_name);
|
||||||
|
|
||||||
|
|
@ -42,7 +35,6 @@ pub fn create_command<T: AsRef<OsStr>>(binary_name: T) -> Result<Command> {
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
#[allow(clippy::disallowed_methods)]
|
|
||||||
let mut cmd = Command::new(full_path);
|
let mut cmd = Command::new(full_path);
|
||||||
cmd.stderr(Stdio::piped())
|
cmd.stderr(Stdio::piped())
|
||||||
.stdout(Stdio::piped())
|
.stdout(Stdio::piped())
|
||||||
|
|
@ -71,7 +63,7 @@ fn internal_exec_cmd<T: AsRef<OsStr> + Debug, U: AsRef<OsStr> + Debug>(
|
||||||
exec_timeout(&mut cmd, time_limit)
|
exec_timeout(&mut cmd, time_limit)
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn exec_timeout(cmd: &mut Command, time_limit: Duration) -> Option<CommandOutput> {
|
fn exec_timeout(cmd: &mut Command, time_limit: Duration) -> Option<CommandOutput> {
|
||||||
let start = Instant::now();
|
let start = Instant::now();
|
||||||
let process = match cmd.spawn() {
|
let process = match cmd.spawn() {
|
||||||
Ok(process) => process,
|
Ok(process) => process,
|
||||||
|
|
@ -1,9 +1,13 @@
|
||||||
use std::{io::Write, process::Command};
|
pub mod command_output;
|
||||||
|
pub mod constants;
|
||||||
|
|
||||||
|
use std::{io::Write, path::PathBuf, process::Command, thread, time::Duration};
|
||||||
|
|
||||||
use anyhow::{anyhow, Result};
|
use anyhow::{anyhow, Result};
|
||||||
use format_serde_error::SerdeError;
|
use format_serde_error::SerdeError;
|
||||||
use log::{debug, trace};
|
use log::{debug, trace};
|
||||||
use std::{thread, time::Duration};
|
|
||||||
|
pub use command_output::*;
|
||||||
|
|
||||||
pub fn check_command_exists(command: &str) -> Result<()> {
|
pub fn check_command_exists(command: &str) -> Result<()> {
|
||||||
trace!("check_command_exists({command})");
|
trace!("check_command_exists({command})");
|
||||||
|
|
@ -68,3 +72,8 @@ where
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[must_use]
|
||||||
|
pub fn home_dir() -> Option<PathBuf> {
|
||||||
|
directories::BaseDirs::new().map(|base_dirs| base_dirs.home_dir().to_path_buf())
|
||||||
|
}
|
||||||
Loading…
Add table
Add a link
Reference in a new issue