fix: add support for alpine image and using either podman or buildah

This commit is contained in:
Gerald Pinder 2023-12-28 16:28:34 -05:00
parent 6c61cab07e
commit 3b07758709
5 changed files with 178 additions and 153 deletions

View file

@ -1,13 +1,13 @@
VERSION --global-cache 0.7
IMPORT github.com/earthly/lib/rust AS rust
ARG FEDORA_MAJOR_VERSION=38
FROM registry.fedoraproject.org/fedora-toolbox:${FEDORA_MAJOR_VERSION}
ARG --global FEDORA_MAJOR_VERSION=38
ARG --global IMAGE=registry.gitlab.com/wunker-bunker/ublue-cli
iso-generator:
FROM registry.fedoraproject.org/fedora-toolbox:${FEDORA_MAJOR_VERSION}
GIT CLONE https://github.com/ublue-os/isogenerator.git /isogenerator
WORKDIR /isogenerator
ARG PACKAGES=$(cat deps.txt)
@ -15,6 +15,10 @@ iso-generator:
SAVE IMAGE --push $IMAGE/iso-generator
cosign:
FROM gcr.io/projectsigstore/cosign
SAVE ARTIFACT /ko-app/cosign
install:
FROM rust
DO rust+INIT --keep_fingerprints=true
@ -22,14 +26,19 @@ install:
COPY --keep-ts . /app
WORKDIR /app
DO rust+CARGO --args="build --release" --output="release/[^\./]+"
ARG --required TARGET
DO rust+CARGO --args="build --release --target $TARGET" --output="$TARGET/release/[^\./]+"
SAVE ARTIFACT target/release/ublue
SAVE ARTIFACT target/$TARGET/release/ublue
ublue-cli:
BUILD +install
FROM registry.fedoraproject.org/fedora-toolbox:${FEDORA_MAJOR_VERSION}
BUILD +install --TARGET="x86_64-unknown-linux-gnu"
COPY +install/ublue /usr/bin/ublue
RUN dnf install --refresh -y buildah podman skopeo
COPY +cosign/cosign /usr/bin/cosign
COPY (+install/ublue --TARGET="x86_64-unknown-linux-gnu") /usr/bin/ublue
ARG TAG
IF [ "$TAG" != "" ]
@ -40,8 +49,33 @@ ublue-cli:
IF [ "$LATEST" = "true" ]
SAVE IMAGE --push $IMAGE:latest
END
ELSE
SAVE IMAGE ublue-cli
END
ublue-cli-alpine:
FROM alpine
BUILD +install --TARGET="x86_64-unknown-linux-musl"
RUN apk update && apk add buildah podman skopeo fuse-overlayfs
COPY +cosign/cosign /usr/bin/cosign
COPY (+install/ublue --TARGET="x86_64-unknown-linux-musl") /usr/bin/ublue
ARG TAG
IF [ "$TAG" != "" ]
SAVE IMAGE --push $IMAGE:$TAG-alpine
ARG LATEST=false
IF [ "$LATEST" = "true" ]
SAVE IMAGE --push $IMAGE:alpine
END
ELSE
SAVE IMAGE ublue-cli:alpine
END
all:
BUILD +ublue-cli
BUILD +ublue-cli-alpine
BUILD +iso-generator

View file

@ -5,7 +5,6 @@ use std::{
};
use anyhow::{anyhow, bail, Result};
use chrono::Local;
use clap::Args;
use log::{debug, error, info, trace, warn};
use typed_builder::TypedBuilder;
@ -82,7 +81,11 @@ impl BuildCommand {
fn build_image(&self) -> Result<()> {
trace!("BuildCommand::build_image()");
ops::check_command_exists("buildah")?;
if let Err(e1) = ops::check_command_exists("buildah") {
ops::check_command_exists("podman").map_err(|e2| {
anyhow!("Need either 'buildah' or 'podman' commands to proceed: {e1}, {e2}")
})?;
}
if self.push {
ops::check_command_exists("cosign")?;
@ -91,7 +94,7 @@ impl BuildCommand {
let recipe: Recipe = serde_yaml::from_str(fs::read_to_string(&self.recipe)?.as_str())?;
let tags = self.generate_tags(&recipe);
let tags = recipe.generate_tags();
let image_name = self.generate_full_image_name(&recipe)?;
@ -105,57 +108,6 @@ impl BuildCommand {
Ok(())
}
fn generate_tags(&self, recipe: &Recipe) -> Vec<String> {
debug!("Generating image tags for {}", &recipe.name);
trace!("BuildCommand::generate_tags({recipe:#?})");
let mut tags: Vec<String> = Vec::new();
let image_version = recipe.image_version;
let timestamp = Local::now().format("%Y%m%d").to_string();
if env::var("CI").is_ok() {
warn!("Detected running in Gitlab, pulling information from CI variables");
if let (Ok(mr_iid), Ok(pipeline_source)) = (
env::var("CI_MERGE_REQUEST_IID"),
env::var("CI_PIPELINE_SOURCE"),
) {
trace!("CI_MERGE_REQUEST_IID={mr_iid}, CI_PIPELINE_SOURCE={pipeline_source}");
if pipeline_source == "merge_request_event" {
debug!("Running in a MR");
tags.push(format!("{mr_iid}-{image_version}"));
}
}
if let Ok(commit_sha) = env::var("CI_COMMIT_SHORT_SHA") {
trace!("CI_COMMIT_SHORT_SHA={commit_sha}");
tags.push(format!("{commit_sha}-{image_version}"));
}
if let (Ok(commit_branch), Ok(default_branch)) = (
env::var("CI_COMMIT_REF_NAME"),
env::var("CI_DEFAULT_BRANCH"),
) {
trace!("CI_COMMIT_REF_NAME={commit_branch}, CI_DEFAULT_BRANCH={default_branch}");
if default_branch != commit_branch {
debug!("Running on branch {commit_branch}");
tags.push(format!("br-{commit_branch}-{image_version}"));
} else {
debug!("Running on the default branch");
tags.push(image_version.to_string());
tags.push(format!("{image_version}-{timestamp}"));
tags.push(timestamp.to_string());
}
}
} else {
warn!("Running locally");
tags.push(format!("{image_version}-local"));
}
info!("Finished generating tags!");
trace!("Tags: {tags:#?}");
tags
}
fn login(&self) -> Result<()> {
info!("Attempting to login to the registry");
trace!("BuildCommand::login()");
@ -183,11 +135,12 @@ impl BuildCommand {
.arg("-p")
.arg(&password)
.arg(&registry)
.status()?
.output()?
.status
.success()
{
true => info!("Buildah login success at {registry} for user {username}!"),
false => return Err(anyhow!("Failed to login for buildah!")),
false => bail!("Failed to login for buildah!"),
}
trace!("cosign login -u {username} -p [MASKED] {registry}");
@ -198,11 +151,12 @@ impl BuildCommand {
.arg("-p")
.arg(&password)
.arg(&registry)
.status()?
.output()?
.status
.success()
{
true => info!("Cosign login success at {registry} for user {username}!"),
false => return Err(anyhow!("Failed to login for cosign!")),
false => bail!("Failed to login for cosign!"),
}
Ok(())
@ -260,12 +214,29 @@ impl BuildCommand {
let full_image = format!("{image_name}:{first_tag}");
trace!("buildah build -t {full_image}");
let status = Command::new("buildah")
.arg("build")
.arg("-t")
.arg(&full_image)
.status()?;
let status = match (
ops::check_command_exists("buildah"),
ops::check_command_exists("podman"),
) {
(Ok(_), _) => {
trace!("buildah build -t {full_image}");
Command::new("buildah")
.arg("build")
.arg("-t")
.arg(&full_image)
.status()?
}
(Err(_), Ok(_)) => {
trace!("podman build . -t {full_image}");
Command::new("podman")
.arg("build")
.arg(".")
.arg("-t")
.arg(&full_image)
.status()?
}
(Err(e1), Err(e2)) => bail!("Need either 'buildah' or 'podman' to build: {e1}, {e2}"),
};
if status.success() {
info!("Successfully built {image_name}");
@ -281,12 +252,26 @@ impl BuildCommand {
let tag_image = format!("{image_name}:{tag}");
trace!("buildah tag {full_image} {tag_image}");
let status = Command::new("buildah")
.arg("tag")
.arg(&full_image)
.arg(&tag_image)
.status()?;
let status = match (
ops::check_command_exists("buildah"),
ops::check_command_exists("podman"),
) {
(Ok(_), _) => {
trace!("buildah tag {full_image} {tag_image}");
Command::new("buildah")
}
(Err(_), Ok(_)) => {
trace!("podman tag {full_image} {tag_image}");
Command::new("podman")
}
(Err(e1), Err(e2)) => {
bail!("Need either 'buildah' or 'podman' to build: {e1}, {e2}")
}
}
.arg("tag")
.arg(&full_image)
.arg(&tag_image)
.status()?;
if status.success() {
info!("Successfully tagged {image_name}:{tag}!");
@ -303,11 +288,25 @@ impl BuildCommand {
let tag_image = format!("{image_name}:{tag}");
trace!("buildah push {tag_image}");
let status = Command::new("buildah")
.arg("push")
.arg(&tag_image)
.status()?;
let status = match (
ops::check_command_exists("buildah"),
ops::check_command_exists("podman"),
) {
(Ok(_), _) => {
trace!("buildah push {tag_image}");
Command::new("buildah")
}
(Err(_), Ok(_)) => {
trace!("podman push {tag_image}");
Command::new("podman")
}
(Err(e1), Err(e2)) => {
bail!("Need either 'buildah' or 'podman' to build: {e1}, {e2}")
}
}
.arg("push")
.arg(&tag_image)
.status()?;
if status.success() {
info!("Successfully pushed {image_name}:{tag}!")
@ -397,6 +396,9 @@ impl BuildCommand {
if !status.success() {
bail!("Failed to verify image!");
}
} else {
warn!("Unable to determine OIDC host, not signing image");
warn!("Please ensure your build environment has the variables CI_PROJECT_URL, CI_DEFAULT_BRANCH, CI_COMMIT_REF_NAME, CI_SERVER_PROTOCOL, CI_SERVER_HOST")
}
}
} else {

View file

@ -1,5 +1,7 @@
use std::collections::HashMap;
use std::{collections::HashMap, env};
use chrono::Local;
use log::{debug, info, trace, warn};
use serde::{Deserialize, Serialize};
use serde_yaml::Value;
@ -23,6 +25,59 @@ pub struct Recipe {
pub extra: HashMap<String, Value>,
}
impl Recipe {
pub fn generate_tags(&self) -> Vec<String> {
debug!("Generating image tags for {}", &self.name);
trace!("BuildCommand::generate_tags({self:#?})");
let mut tags: Vec<String> = Vec::new();
let image_version = self.image_version;
let timestamp = Local::now().format("%Y%m%d").to_string();
if env::var("CI").is_ok() {
warn!("Detected running in Gitlab, pulling information from CI variables");
if let (Ok(mr_iid), Ok(pipeline_source)) = (
env::var("CI_MERGE_REQUEST_IID"),
env::var("CI_PIPELINE_SOURCE"),
) {
trace!("CI_MERGE_REQUEST_IID={mr_iid}, CI_PIPELINE_SOURCE={pipeline_source}");
if pipeline_source == "merge_request_event" {
debug!("Running in a MR");
tags.push(format!("{mr_iid}-{image_version}"));
}
}
if let Ok(commit_sha) = env::var("CI_COMMIT_SHORT_SHA") {
trace!("CI_COMMIT_SHORT_SHA={commit_sha}");
tags.push(format!("{commit_sha}-{image_version}"));
}
if let (Ok(commit_branch), Ok(default_branch)) = (
env::var("CI_COMMIT_REF_NAME"),
env::var("CI_DEFAULT_BRANCH"),
) {
trace!("CI_COMMIT_REF_NAME={commit_branch}, CI_DEFAULT_BRANCH={default_branch}");
if default_branch != commit_branch {
debug!("Running on branch {commit_branch}");
tags.push(format!("br-{commit_branch}-{image_version}"));
} else {
debug!("Running on the default branch");
tags.push(image_version.to_string());
tags.push(format!("{image_version}-{timestamp}"));
tags.push(timestamp.to_string());
}
}
} else {
warn!("Running locally");
tags.push(format!("{image_version}-local"));
}
info!("Finished generating tags!");
trace!("Tags: {tags:#?}");
tags
}
}
#[derive(Serialize, Deserialize, Debug)]
pub struct Module {
#[serde(rename = "type")]

View file

@ -7,11 +7,11 @@ pub fn check_command_exists(command: &str) -> Result<()> {
debug!("Checking if {command} exists");
trace!("check_command_exists({command})");
trace!("command -v {command}");
match Command::new("command")
.arg("-v")
trace!("which {command}");
match Command::new("which")
.arg(command)
.status()?
.output()?
.status
.success()
{
true => {

View file

@ -1,66 +0,0 @@
use serde::{Deserialize, Serialize};
#[derive(Serialize, Deserialize, Debug)]
pub struct Recipe {
pub name: String,
#[serde(alias = "base-image")]
pub base_image: String,
#[serde(alias = "fedora-version")]
pub fedora_version: u16,
pub scripts: Option<Scripts>,
pub rpm: Option<Rpm>,
#[serde(alias = "usr-dir-overlays")]
pub usr_dir_overlays: Option<Vec<String>>,
pub containerfiles: Option<Containerfiles>,
pub firstboot: Option<FirstBoot>,
}
impl Recipe {
pub fn process_repos(mut self) -> Self {
if let Some(rpm) = &mut self.rpm {
if let Some(repos) = &rpm.repos {
rpm.repos = Some(
repos
.iter()
.map(|s| {
s.replace("%FEDORA_VERSION%", self.fedora_version.to_string().as_str())
})
.collect(),
);
}
}
self
}
}
#[derive(Debug, Serialize, Deserialize)]
pub struct Scripts {
pub pre: Option<Vec<String>>,
pub post: Option<Vec<String>>,
}
#[derive(Debug, Serialize, Deserialize)]
pub struct Rpm {
pub repos: Option<Vec<String>>,
pub install: Option<Vec<String>>,
pub remove: Option<Vec<String>>,
}
#[derive(Debug, Serialize, Deserialize)]
pub struct FirstBoot {
pub yafti: bool,
pub flatpaks: Option<Vec<String>>,
}
#[derive(Debug, Serialize, Deserialize)]
pub struct Containerfiles {
pub pre: Option<Vec<String>>,
pub post: Option<Vec<String>>,
}