feat: block overriding (#74)
This PR helps transition users who may not realize that we override their Containefile. --------- Co-authored-by: Gerald Pinder <gmpinder@gmail.com>
This commit is contained in:
parent
84de477635
commit
ee2a834b28
5 changed files with 116 additions and 12 deletions
12
Cargo.toml
12
Cargo.toml
|
|
@ -35,7 +35,9 @@ serde = { version = "1", features = ["derive"] }
|
|||
serde_json = "1"
|
||||
serde_yaml = "0.9.30"
|
||||
signal-hook = { version = "0.3.17", optional = true }
|
||||
signal-hook-tokio = { version = "0.3.1", features = ["futures-v0_3"], optional = true }
|
||||
signal-hook-tokio = { version = "0.3.1", features = [
|
||||
"futures-v0_3",
|
||||
], optional = true }
|
||||
shadow-rs = { version = "0.26" }
|
||||
sigstore = { version = "0.8.0", optional = true }
|
||||
tokio = { version = "1", features = ["full"], optional = true }
|
||||
|
|
@ -48,7 +50,13 @@ which = "6"
|
|||
[features]
|
||||
default = []
|
||||
nightly = ["builtin-podman"]
|
||||
builtin-podman = ["podman-api", "tokio", "futures-util", "signal-hook-tokio", "signal-hook"]
|
||||
builtin-podman = [
|
||||
"podman-api",
|
||||
"tokio",
|
||||
"futures-util",
|
||||
"signal-hook-tokio",
|
||||
"signal-hook",
|
||||
]
|
||||
tls = ["podman-api/tls", "builtin-podman"]
|
||||
|
||||
[dev-dependencies]
|
||||
|
|
|
|||
|
|
@ -9,6 +9,7 @@ use std::{
|
|||
|
||||
use anyhow::{anyhow, bail, Result};
|
||||
use clap::Args;
|
||||
use colorized::{Color, Colors};
|
||||
use log::{debug, info, trace, warn};
|
||||
use typed_builder::TypedBuilder;
|
||||
use uuid::Uuid;
|
||||
|
|
@ -34,7 +35,12 @@ use tokio::{
|
|||
sync::oneshot::{self, Sender},
|
||||
};
|
||||
|
||||
use crate::{commands::template::TemplateCommand, constants::*, module_recipe::Recipe, ops};
|
||||
use crate::{
|
||||
commands::template::TemplateCommand,
|
||||
constants::{self, *},
|
||||
module_recipe::Recipe,
|
||||
ops,
|
||||
};
|
||||
|
||||
use super::BlueBuildCommand;
|
||||
|
||||
|
|
@ -61,6 +67,15 @@ pub struct BuildCommand {
|
|||
#[builder(default)]
|
||||
push: bool,
|
||||
|
||||
/// Allow `bluebuild` to overwrite an existing
|
||||
/// Containerfile without confirmation.
|
||||
///
|
||||
/// This is not needed if the Containerfile is in
|
||||
/// .gitignore or has already been built by `bluebuild`.
|
||||
#[arg(short, long)]
|
||||
#[builder(default)]
|
||||
force: bool,
|
||||
|
||||
/// Archives the built image into a tarfile
|
||||
/// in the specified directory.
|
||||
#[arg(short, long)]
|
||||
|
|
@ -146,8 +161,56 @@ impl BlueBuildCommand for BuildCommand {
|
|||
fn try_run(&mut self) -> Result<()> {
|
||||
trace!("BuildCommand::try_run()");
|
||||
|
||||
let build_id = Uuid::new_v4();
|
||||
// Check if the Containerfile exists
|
||||
// - If doesn't => *Build*
|
||||
// - If it does:
|
||||
// - check entry in .gitignore
|
||||
// -> If it is => *Build*
|
||||
// -> If isn't:
|
||||
// - check if it has the BlueBuild tag (LABEL)
|
||||
// -> If it does => *Ask* to add to .gitignore and remove from git
|
||||
// -> If it doesn't => *Ask* to continue and override the file
|
||||
|
||||
let container_file_path = Path::new(constants::CONTAINER_FILE);
|
||||
|
||||
if !self.force && container_file_path.exists() {
|
||||
let gitignore = fs::read_to_string(constants::GITIGNORE_PATH)?;
|
||||
|
||||
let is_ignored = gitignore
|
||||
.lines()
|
||||
.any(|line: &str| line.contains(constants::CONTAINER_FILE));
|
||||
|
||||
if !is_ignored {
|
||||
let containerfile = fs::read_to_string(container_file_path)?;
|
||||
let has_label = containerfile.lines().any(|line| {
|
||||
let label = format!("LABEL {}", constants::BUILD_ID_LABEL);
|
||||
line.to_string().trim().starts_with(&label)
|
||||
});
|
||||
|
||||
let question = requestty::Question::confirm("build")
|
||||
.message(
|
||||
if has_label {
|
||||
LABELED_ERROR_MESSAGE
|
||||
} else {
|
||||
NO_LABEL_ERROR_MESSAGE
|
||||
}
|
||||
.color(Colors::BrightYellowFg),
|
||||
)
|
||||
.default(true)
|
||||
.build();
|
||||
|
||||
if let Ok(answer) = requestty::prompt_one(question) {
|
||||
if answer.as_bool().unwrap_or(false) {
|
||||
ops::append_to_file(
|
||||
constants::GITIGNORE_PATH,
|
||||
&format!("/{}", constants::CONTAINER_FILE),
|
||||
)?;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
let build_id = Uuid::new_v4();
|
||||
if self.push && self.archive.is_some() {
|
||||
bail!("You cannot use '--archive' and '--push' at the same time");
|
||||
}
|
||||
|
|
@ -223,8 +286,7 @@ impl BuildCommand {
|
|||
image_name.to_string()
|
||||
} else {
|
||||
tags.first()
|
||||
.map(|t| format!("{image_name}:{t}"))
|
||||
.unwrap_or_else(|| image_name.to_string())
|
||||
.map_or_else(|| image_name.to_string(), |t| format!("{image_name}:{t}"))
|
||||
};
|
||||
debug!("Full tag is {first_image_name}");
|
||||
|
||||
|
|
@ -473,8 +535,7 @@ impl BuildCommand {
|
|||
image_name.to_string()
|
||||
} else {
|
||||
tags.first()
|
||||
.map(|t| format!("{image_name}:{t}"))
|
||||
.unwrap_or_else(|| image_name.to_string())
|
||||
.map_or_else(|| image_name.to_string(), |t| format!("{image_name}:{t}"))
|
||||
};
|
||||
|
||||
info!("Building image {full_image}");
|
||||
|
|
@ -575,9 +636,7 @@ fn sign_images(image_name: &str, tag: Option<&str>) -> Result<()> {
|
|||
env::set_var("COSIGN_YES", "true");
|
||||
|
||||
let image_digest = get_image_digest(image_name, tag)?;
|
||||
let image_name_tag = tag
|
||||
.map(|t| format!("{image_name}:{t}"))
|
||||
.unwrap_or_else(|| image_name.to_owned());
|
||||
let image_name_tag = tag.map_or_else(|| image_name.to_owned(), |t| format!("{image_name}:{t}"));
|
||||
|
||||
match (
|
||||
env::var(CI_DEFAULT_BRANCH),
|
||||
|
|
|
|||
|
|
@ -30,6 +30,15 @@ pub struct LocalCommonArgs {
|
|||
#[arg(short, long)]
|
||||
#[builder(default)]
|
||||
reboot: bool,
|
||||
|
||||
/// Allow `bluebuild` to overwrite an existing
|
||||
/// Containerfile without confirmation.
|
||||
///
|
||||
/// This is not needed if the Containerfile is in
|
||||
/// .gitignore or has already been built by `bluebuild`.
|
||||
#[arg(short, long)]
|
||||
#[builder(default)]
|
||||
force: bool,
|
||||
}
|
||||
|
||||
#[derive(Default, Clone, Debug, TypedBuilder, Args)]
|
||||
|
|
@ -49,6 +58,7 @@ impl BlueBuildCommand for UpgradeCommand {
|
|||
let mut build = BuildCommand::builder()
|
||||
.recipe(self.common.recipe.clone())
|
||||
.archive(LOCAL_BUILD)
|
||||
.force(self.common.force)
|
||||
.build();
|
||||
|
||||
let image_name = build.generate_full_image_name(&recipe)?;
|
||||
|
|
@ -96,6 +106,7 @@ impl BlueBuildCommand for RebaseCommand {
|
|||
let mut build = BuildCommand::builder()
|
||||
.recipe(self.common.recipe.clone())
|
||||
.archive(LOCAL_BUILD)
|
||||
.force(self.common.force)
|
||||
.build();
|
||||
|
||||
let image_name = build.generate_full_image_name(&recipe)?;
|
||||
|
|
|
|||
|
|
@ -1,7 +1,9 @@
|
|||
// Paths
|
||||
pub const ARCHIVE_SUFFIX: &str = "tar.gz";
|
||||
pub const COSIGN_PATH: &str = "./cosign.pub";
|
||||
pub const GITIGNORE_PATH: &str = ".gitignore";
|
||||
pub const LOCAL_BUILD: &str = "/etc/bluebuild";
|
||||
pub const CONTAINER_FILE: &str = "Containerfile";
|
||||
pub const MODULES_PATH: &str = "./config/modules";
|
||||
pub const RECIPE_PATH: &str = "./config/recipe.yml";
|
||||
pub const RUN_PODMAN_SOCK: &str = "/run/podman/podman.sock";
|
||||
|
|
@ -54,3 +56,13 @@ pub const UNKNOWN_SHELL: &str = "<unknown shell>";
|
|||
pub const UNKNOWN_VERSION: &str = "<unknown version>";
|
||||
pub const UNKNOWN_TERMINAL: &str = "<unknown terminal>";
|
||||
pub const GITHUB_CHAR_LIMIT: usize = 8100; // Magic number accepted by Github
|
||||
|
||||
// Messages
|
||||
pub const LABELED_ERROR_MESSAGE: &str =
|
||||
"It looks you have a BlueBuild-generated Containerfile that is not .gitignored. \
|
||||
Do you want to remove it from git and add it to .gitignore? (This will still continue the build)";
|
||||
|
||||
pub const NO_LABEL_ERROR_MESSAGE: &str =
|
||||
"It looks you have a Containerfile that has not been generated by BlueBuild. \
|
||||
Running `build` will override your Containerfile and add an entry to the .gitignore. \
|
||||
Do you want to continue?";
|
||||
|
|
|
|||
16
src/ops.rs
16
src/ops.rs
|
|
@ -1,7 +1,8 @@
|
|||
use std::{io::Write, process::Command};
|
||||
|
||||
use anyhow::{anyhow, Result};
|
||||
use format_serde_error::SerdeError;
|
||||
use log::{debug, trace};
|
||||
use std::process::Command;
|
||||
|
||||
pub fn check_command_exists(command: &str) -> Result<()> {
|
||||
trace!("check_command_exists({command})");
|
||||
|
|
@ -23,6 +24,19 @@ pub fn check_command_exists(command: &str) -> Result<()> {
|
|||
}
|
||||
}
|
||||
|
||||
pub fn append_to_file(file_path: &str, content: &str) -> Result<()> {
|
||||
trace!("append_to_file({file_path}, {content})");
|
||||
debug!("Appending {content} to {file_path}");
|
||||
|
||||
let mut file = std::fs::OpenOptions::new()
|
||||
.append(true)
|
||||
.create(true)
|
||||
.open(file_path)?;
|
||||
|
||||
writeln!(file, "\n{content}")?;
|
||||
Ok(())
|
||||
}
|
||||
|
||||
pub fn serde_yaml_err(contents: &str) -> impl Fn(serde_yaml::Error) -> SerdeError + '_ {
|
||||
|err: serde_yaml::Error| {
|
||||
let location = err.location();
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue