Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

feat: Support versioned modules #311

Draft
wants to merge 1 commit into
base: refactor-module-usage
Choose a base branch
from
Draft
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
25 changes: 18 additions & 7 deletions recipe/src/module.rs
Original file line number Diff line number Diff line change
Expand Up @@ -11,11 +11,15 @@ use serde_yaml::Value;

use crate::{base_recipe_path, AkmodsInfo, ModuleExt};

#[derive(Serialize, Deserialize, Debug, Clone, Builder, Default)]
mod type_ver;

pub use type_ver::*;

#[derive(Serialize, Deserialize, Debug, Clone, Builder)]
pub struct ModuleRequiredFields<'a> {
#[builder(into)]
#[serde(rename = "type")]
pub module_type: Cow<'a, str>,
pub module_type: ModuleTypeVersion<'a>,

#[builder(into)]
#[serde(skip_serializing_if = "Option::is_none")]
Expand All @@ -38,7 +42,7 @@ const fn is_false(b: &bool) -> bool {
impl<'a> ModuleRequiredFields<'a> {
#[must_use]
pub fn get_module_type_list(&'a self, typ: &str, list_key: &str) -> Option<Vec<String>> {
if self.module_type == typ {
if self.module_type.typ() == typ {
Some(
self.config
.get(list_key)?
Expand Down Expand Up @@ -217,7 +221,7 @@ impl Module<'_> {
required_fields: None,
from_file: Some(file_name),
} => {
let file_name = PathBuf::from(file_name.as_ref());
let file_name = PathBuf::from(&**file_name);
if traversed_files.contains(&file_name) {
bail!(
"{} File {} has already been parsed:\n{traversed_files:?}",
Expand Down Expand Up @@ -260,14 +264,21 @@ impl Module<'_> {
}

#[must_use]
#[allow(clippy::missing_panics_doc)]
pub fn example() -> Self {
Self::builder()
.required_fields(
ModuleRequiredFields::builder()
.module_type("module-name")
.module_type("script")
.config(IndexMap::from_iter([
("module".to_string(), Value::String("config".to_string())),
("goes".to_string(), Value::String("here".to_string())),
(
"snippets".to_string(),
Value::Sequence(bon::vec!["echo 'Hello World!'"]),
),
(
"scripts".to_string(),
Value::Sequence(bon::vec!["install-program.sh"]),
),
]))
.build(),
)
Expand Down
78 changes: 78 additions & 0 deletions recipe/src/module/type_ver.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,78 @@
use std::borrow::Cow;

use serde::{Deserialize, Deserializer, Serialize};

#[derive(Debug, Clone)]
pub struct ModuleTypeVersion<'scope> {
typ: Cow<'scope, str>,
version: Cow<'scope, str>,
}

impl<'scope> ModuleTypeVersion<'scope> {
#[must_use]
pub fn typ(&self) -> &str {
&self.typ
}

#[must_use]
pub fn version(&self) -> &str {
&self.version
}
}

impl std::fmt::Display for ModuleTypeVersion<'_> {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
write!(f, "{}@{}", &self.typ, &self.version)
}
}

impl<'scope> From<&'scope str> for ModuleTypeVersion<'scope> {
fn from(s: &'scope str) -> Self {
if let Some((typ, version)) = s.split_once('@') {
Self {
typ: Cow::Borrowed(typ),
version: Cow::Borrowed(version),
}
} else {
Self {
typ: Cow::Borrowed(s),
version: Cow::Owned("latest".into()),
}
}
}
}

impl From<String> for ModuleTypeVersion<'_> {
fn from(s: String) -> Self {
if let Some((typ, version)) = s.split_once('@') {
Self {
typ: Cow::Owned(typ.to_owned()),
version: Cow::Owned(version.to_owned()),
}
} else {
Self {
typ: Cow::Owned(s),
version: Cow::Owned("latest".into()),
}
}
}
}

impl Serialize for ModuleTypeVersion<'_> {
fn serialize<S>(&self, serializer: S) -> Result<S::Ok, S::Error>
where
S: serde::Serializer,
{
serializer.serialize_str(&self.to_string())
}
}

impl<'de> Deserialize<'de> for ModuleTypeVersion<'_> {
fn deserialize<D>(deserializer: D) -> Result<Self, D::Error>
where
D: Deserializer<'de>,
{
let value: String = Deserialize::deserialize(deserializer)?;
Ok(value.into())
}
}
2 changes: 1 addition & 1 deletion recipe/src/module_ext.rs
Original file line number Diff line number Diff line change
Expand Up @@ -72,7 +72,7 @@ impl ModuleExt<'_> {
module
.required_fields
.as_ref()
.is_some_and(|rf| rf.module_type == "akmods")
.is_some_and(|rf| rf.module_type.typ() == "akmods")
})
.filter_map(|module| {
Some(
Expand Down
2 changes: 1 addition & 1 deletion recipe/src/stage.rs
Original file line number Diff line number Diff line change
Expand Up @@ -108,7 +108,7 @@ impl Stage<'_> {
required_fields: None,
from_file: Some(file_name),
} => {
let file_name = PathBuf::from(file_name.as_ref());
let file_name = PathBuf::from(&**file_name);
if traversed_files.contains(&file_name) {
bail!(
"{} File {} has already been parsed:\n{traversed_files:?}",
Expand Down
18 changes: 9 additions & 9 deletions template/templates/modules/modules.j2
Original file line number Diff line number Diff line change
Expand Up @@ -6,9 +6,9 @@
ARG CACHEBUST="{{ build_id }}"
{%- endif %}

{%- if module.module_type == "containerfile" %}
{%- if module.module_type.typ() == "containerfile" %}
{%- include "modules/containerfile/containerfile.j2" %}
{%- else if module.module_type == "copy" %}
{%- else if module.module_type.typ() == "copy" %}
{%- include "modules/copy/copy.j2" %}
{%- else %}
RUN \
Expand All @@ -22,15 +22,15 @@ RUN \
{%- else if module.is_local_source() %}
--mount=type=bind,from=stage-modules,src=/modules,dst=/tmp/modules,rw \
{%- else %}
--mount=type=bind,from=ghcr.io/blue-build/modules/{{ module.module_type }}:latest,src=/modules,dst=/tmp/modules,rw \
--mount=type=bind,from={{ blue_build_utils::constants::BLUE_BUILD_MODULE_IMAGE_REF }}/{{ module.module_type.typ() }}:{{ module.module_type.version() }},src=/modules,dst=/tmp/modules,rw \
{%- endif %}
{%- if module.module_type == "akmods" %}
{%- if module.module_type.typ() == "akmods" %}
--mount=type=bind,from=stage-akmods-{{ module.generate_akmods_info(os_version).stage_name }},src=/rpms,dst=/tmp/rpms,rw \
{%- endif %}
--mount=type=bind,from={{ build_scripts_image }},src=/scripts/,dst=/tmp/scripts/ \
--mount=type=cache,dst=/var/cache/rpm-ostree,id=rpm-ostree-cache-{{ recipe.name }}-{{ recipe.image_version }},sharing=locked \
--mount=type=cache,dst=/var/cache/libdnf5,id=dnf-cache-{{ recipe.name }}-{{ recipe.image_version }},sharing=locked \
/tmp/scripts/run_module.sh '{{ module.module_type }}' '{{ module|json|safe }}'
/tmp/scripts/run_module.sh '{{ module.module_type.typ() }}' '{{ module|json|safe }}'
{%- endif %}
{%- endif %}
{%- endfor %}
Expand All @@ -45,9 +45,9 @@ RUN \
ARG CACHEBUST="{{ build_id }}"
{%- endif %}

{%- if module.module_type == "containerfile" %}
{%- if module.module_type.typ() == "containerfile" %}
{%- include "modules/containerfile/containerfile.j2" %}
{%- else if module.module_type == "copy" %}
{%- else if module.module_type.typ() == "copy" %}
{%- include "modules/copy/copy.j2" %}
{%- else %}
RUN \
Expand All @@ -61,10 +61,10 @@ RUN \
{%- else if module.is_local_source() %}
--mount=type=bind,from=stage-modules,src=/modules,dst=/tmp/modules,rw \
{%- else %}
--mount=type=bind,from=ghcr.io/blue-build/modules/{{ module.module_type }}:latest,src=/modules,dst=/tmp/modules,rw \
--mount=type=bind,from={{ blue_build_utils::constants::BLUE_BUILD_MODULE_IMAGE_REF }}/{{ module.module_type.typ() }}:{{ module.module_type.version() }},src=/modules,dst=/tmp/modules,rw \
{%- endif %}
--mount=type=bind,from={{ build_scripts_image }},src=/scripts/,dst=/tmp/scripts/ \
/tmp/scripts/run_module.sh '{{ module.module_type }}' '{{ module|json|safe }}'
/tmp/scripts/run_module.sh '{{ module.module_type.typ() }}' '{{ module|json|safe }}'
{%- endif %}
{%- endif %}
{%- endfor %}
Expand Down
1 change: 1 addition & 0 deletions utils/src/constants.rs
Original file line number Diff line number Diff line change
Expand Up @@ -75,6 +75,7 @@ pub const XDG_RUNTIME_DIR: &str = "XDG_RUNTIME_DIR";
// Misc
pub const BUILD_SCRIPTS_IMAGE_REF: &str = "ghcr.io/blue-build/cli/build-scripts";
pub const BLUE_BULID_IMAGE_REF: &str = "ghcr.io/blue-build/cli";
pub const BLUE_BUILD_MODULE_IMAGE_REF: &str = "ghcr.io/blue-build/modules";
pub const COSIGN_IMAGE: &str = "ghcr.io/sigstore/cosign/cosign:v2.4.1";
pub const NUSHELL_IMAGE: &str = "ghcr.io/blue-build/nushell-image:latest";
pub const OCI_ARCHIVE: &str = "oci-archive";
Expand Down
Loading