Initial commit

This commit is contained in:
Adam Štrauch 2025-04-28 00:06:25 +02:00
commit 8df44d76d1
Signed by: cx
GPG key ID: 7262DAFE292BCE20
9 changed files with 897 additions and 0 deletions

4
.gitignore vendored Normal file
View file

@ -0,0 +1,4 @@
cosign.key
_build_*
output
_build-*/**

31
Containerfile Normal file
View file

@ -0,0 +1,31 @@
# Allow build scripts to be referenced without being copied into the final image
FROM scratch AS ctx
COPY build_files /
# Base Image
# FROM ghcr.io/ublue-os/bazzite:stable
FROM quay.io/fedora/fedora-bootc:42
## Other possible base images include:
# FROM ghcr.io/ublue-os/bazzite:latest
# FROM ghcr.io/ublue-os/bluefin-nvidia:stable
#
# ... and so on, here are more base images
# Universal Blue Images: https://github.com/orgs/ublue-os/packages
# Fedora base image: quay.io/fedora/fedora-bootc:41
# CentOS base images: quay.io/centos-bootc/centos-bootc:stream10
### MODIFICATIONS
## make modifications desired in your image and install packages by modifying the build.sh script
## the following RUN directive does all the things required to run "build.sh" as recommended.
RUN --mount=type=bind,from=ctx,source=/,target=/ctx \
--mount=type=cache,dst=/var/cache \
--mount=type=cache,dst=/var/log \
--mount=type=tmpfs,dst=/tmp \
/ctx/build.sh && \
ostree container commit
### LINTING
## Verify final image and contents are correct.
RUN bootc container lint

332
Justfile Normal file
View file

@ -0,0 +1,332 @@
export repo_organization := env("GITHUB_REPOSITORY_OWNER", "cx")
export image_name := env("IMAGE_NAME", "fedora-coreos-incus-image")
export centos_version := env("CENTOS_VERSION", "stream10")
export fedora_version := env("CENTOS_VERSION", "42")
export default_tag := env("DEFAULT_TAG", "latest")
export bib_image := env("BIB_IMAGE", "quay.io/centos-bootc/bootc-image-builder:latest")
alias build-vm := build-qcow2
alias rebuild-vm := rebuild-qcow2
alias run-vm := run-vm-qcow2
[private]
default:
@just --list
# Check Just Syntax
[group('Just')]
check:
#!/usr/bin/bash
find . -type f -name "*.just" | while read -r file; do
echo "Checking syntax: $file"
just --unstable --fmt --check -f $file
done
echo "Checking syntax: Justfile"
just --unstable --fmt --check -f Justfile
# Fix Just Syntax
[group('Just')]
fix:
#!/usr/bin/bash
find . -type f -name "*.just" | while read -r file; do
echo "Checking syntax: $file"
just --unstable --fmt -f $file
done
echo "Checking syntax: Justfile"
just --unstable --fmt -f Justfile || { exit 1; }
# Clean Repo
[group('Utility')]
clean:
#!/usr/bin/bash
set -eoux pipefail
touch _build
find *_build* -exec rm -rf {} \;
rm -f previous.manifest.json
rm -f changelog.md
rm -f output.env
rm -f output/
# Sudo Clean Repo
[group('Utility')]
[private]
sudo-clean:
just sudoif just clean
# sudoif bash function
[group('Utility')]
[private]
sudoif command *args:
#!/usr/bin/bash
function sudoif(){
if [[ "${UID}" -eq 0 ]]; then
"$@"
elif [[ "$(command -v sudo)" && -n "${SSH_ASKPASS:-}" ]] && [[ -n "${DISPLAY:-}" || -n "${WAYLAND_DISPLAY:-}" ]]; then
/usr/bin/sudo --askpass "$@" || exit 1
elif [[ "$(command -v sudo)" ]]; then
/usr/bin/sudo "$@" || exit 1
else
exit 1
fi
}
sudoif {{ command }} {{ args }}
# This Justfile recipe builds a container image using Podman.
#
# Arguments:
# $target_image - The tag you want to apply to the image (default: aurora).
# $tag - The tag for the image (default: lts).
# $dx - Enable DX (default: "0").
# $hwe - Enable HWE (default: "0").
# $gdx - Enable GDX (default: "0").
#
# DX:
# Developer Experience (DX) is a feature that allows you to install the latest developer tools for your system.
# Packages include VScode, Docker, Distrobox, and more.
# HWE:
# Hardware Enablement (HWE) is a feature that allows you to install the latest hardware support for your system.
# Currently this install the Hyperscale SIG kernel which will stay ahead of the CentOS Stream kernel and enables btrfs
# GDX: https://docs.projectaurora.io/gdx/
# GPU Developer Experience (GDX) creates a base as an AI and Graphics platform.
# Installs Nvidia drivers, CUDA, and other tools.
#
# The script constructs the version string using the tag and the current date.
# If the git working directory is clean, it also includes the short SHA of the current HEAD.
#
# just build $target_image $tag $dx $hwe $gdx
#
# Example usage:
# just build aurora lts 1 0 1
#
# This will build an image 'aurora:lts' with DX and GDX enabled.
#
# Build the image using the specified parameters
build $target_image=image_name $tag=default_tag $dx="0" $hwe="0" $gdx="0":
#!/usr/bin/env bash
# Get Version
ver="${tag}-${centos_version}.$(date +%Y%m%d)"
BUILD_ARGS=()
BUILD_ARGS+=("--build-arg" "MAJOR_VERSION=${centos_version}")
BUILD_ARGS+=("--build-arg" "IMAGE_NAME=${target_image}")
BUILD_ARGS+=("--build-arg" "IMAGE_VENDOR=${repo_organization}")
BUILD_ARGS+=("--build-arg" "ENABLE_DX=${dx}")
BUILD_ARGS+=("--build-arg" "ENABLE_HWE=${hwe}")
BUILD_ARGS+=("--build-arg" "ENABLE_GDX=${gdx}")
if [[ -z "$(git status -s)" ]]; then
BUILD_ARGS+=("--build-arg" "SHA_HEAD_SHORT=$(git rev-parse --short HEAD)")
fi
podman build \
"${BUILD_ARGS[@]}" \
--pull=newer \
--tag "${target_image}:${tag}" \
.
# Command: _rootful_load_image
# Description: This script checks if the current user is root or running under sudo. If not, it attempts to resolve the image tag using podman inspect.
# If the image is found, it loads it into rootful podman. If the image is not found, it pulls it from the repository.
#
# Parameters:
# $target_image - The name of the target image to be loaded or pulled.
# $tag - The tag of the target image to be loaded or pulled. Default is 'default_tag'.
#
# Example usage:
# _rootful_load_image my_image latest
#
# Steps:
# 1. Check if the script is already running as root or under sudo.
# 2. Check if target image is in the non-root podman container storage)
# 3. If the image is found, load it into rootful podman using podman scp.
# 4. If the image is not found, pull it from the remote repository into reootful podman.
_rootful_load_image $target_image=image_name $tag=default_tag:
#!/usr/bin/bash
set -eoux pipefail
# Check if already running as root or under sudo
if [[ -n "${SUDO_USER:-}" || "${UID}" -eq "0" ]]; then
echo "Already root or running under sudo, no need to load image from user podman."
exit 0
fi
# Try to resolve the image tag using podman inspect
set +e
resolved_tag=$(podman inspect -t image "${target_image}:${tag}" | jq -r '.[].RepoTags.[0]')
return_code=$?
set -e
USER_IMG_ID=$(podman images --filter reference="${target_image}:${tag}" --format "'{{ '{{.ID}}' }}'")
if [[ $return_code -eq 0 ]]; then
# If the image is found, load it into rootful podman
ID=$(just sudoif podman images --filter reference="${target_image}:${tag}" --format "'{{ '{{.ID}}' }}'")
if [[ "$ID" != "$USER_IMG_ID" ]]; then
# If the image ID is not found or different from user, copy the image from user podman to root podman
COPYTMP=$(mktemp -p "${PWD}" -d -t _build_podman_scp.XXXXXXXXXX)
just sudoif TMPDIR=${COPYTMP} podman image scp ${UID}@localhost::"${target_image}:${tag}" root@localhost::"${target_image}:${tag}"
rm -rf "${COPYTMP}"
fi
else
# If the image is not found, pull it from the repository
just sudoif podman pull "${target_image}:${tag}"
fi
# Build a bootc bootable image using Bootc Image Builder (BIB)
# Converts a container image to a bootable image
# Parameters:
# target_image: The name of the image to build (ex. localhost/fedora)
# tag: The tag of the image to build (ex. latest)
# type: The type of image to build (ex. qcow2, raw, iso)
# config: The configuration file to use for the build (default: image.toml)
# Example: just _rebuild-bib localhost/fedora latest qcow2 image.toml
_build-bib $target_image $tag $type $config: (_rootful_load_image target_image tag)
#!/usr/bin/env bash
set -euo pipefail
args="--type ${type} "
args+="--use-librepo=True "
args+="--rootfs=btrfs"
if [[ $target_image == localhost/* ]]; then
args+=" --local"
fi
BUILDTMP=$(mktemp -p "${PWD}" -d -t _build-bib.XXXXXXXXXX)
sudo podman run \
--rm \
-it \
--privileged \
--pull=newer \
--net=host \
--security-opt label=type:unconfined_t \
-v $(pwd)/${config}:/config.toml:ro \
-v $BUILDTMP:/output \
-v /var/lib/containers/storage:/var/lib/containers/storage \
"${bib_image}" \
${args} \
"${target_image}:${tag}"
mkdir -p output
sudo mv -f $BUILDTMP/* output/
sudo rmdir $BUILDTMP
sudo chown -R $USER:$USER output/
# Podman builds the image from the Containerfile and creates a bootable image
# Parameters:
# target_image: The name of the image to build (ex. localhost/fedora)
# tag: The tag of the image to build (ex. latest)
# type: The type of image to build (ex. qcow2, raw, iso)
# config: The configuration file to use for the build (deafult: image.toml)
# Example: just _rebuild-bib localhost/fedora latest qcow2 image.toml
_rebuild-bib $target_image $tag $type $config: (build target_image tag) && (_build-bib target_image tag type config)
# Build a QCOW2 virtual machine image
[group('Build Virtal Machine Image')]
build-qcow2 $target_image=("localhost/" + image_name) $tag=default_tag: && (_build-bib target_image tag "qcow2" "image.toml")
# Build a RAW virtual machine image
[group('Build Virtal Machine Image')]
build-raw $target_image=("localhost/" + image_name) $tag=default_tag: && (_build-bib target_image tag "raw" "image.toml")
# Build an ISO virtual machine image
[group('Build Virtal Machine Image')]
build-iso $target_image=("localhost/" + image_name) $tag=default_tag: && (_build-bib target_image tag "iso" "iso.toml")
# Rebuild a QCOW2 virtual machine image
[group('Build Virtal Machine Image')]
rebuild-qcow2 $target_image=("localhost/" + image_name) $tag=default_tag: && (_rebuild-bib target_image tag "qcow2" "image.toml")
# Rebuild a RAW virtual machine image
[group('Build Virtal Machine Image')]
rebuild-raw $target_image=("localhost/" + image_name) $tag=default_tag: && (_rebuild-bib target_image tag "raw" "image.toml")
# Rebuild an ISO virtual machine image
[group('Build Virtal Machine Image')]
rebuild-iso $target_image=("localhost/" + image_name) $tag=default_tag: && (_rebuild-bib target_image tag "iso" "iso.toml")
# Run a virtual machine with the specified image type and configuration
_run-vm $target_image $tag $type $config:
#!/usr/bin/bash
set -eoux pipefail
# Determine the image file based on the type
image_file="output/${type}/disk.${type}"
if [[ $type == iso ]]; then
image_file="output/bootiso/install.iso"
fi
# Build the image if it does not exist
if [[ ! -f "${image_file}" ]]; then
just "build-${type}" "$target_image" "$tag"
fi
# Determine an available port to use
port=8006
while grep -q :${port} <<< $(ss -tunalp); do
port=$(( port + 1 ))
done
echo "Using Port: ${port}"
echo "Connect to http://localhost:${port}"
# Set up the arguments for running the VM
run_args=()
run_args+=(--rm --privileged)
run_args+=(--pull=newer)
run_args+=(--publish "127.0.0.1:${port}:8006")
run_args+=(--env "CPU_CORES=4")
run_args+=(--env "RAM_SIZE=8G")
run_args+=(--env "DISK_SIZE=64G")
run_args+=(--env "TPM=Y")
run_args+=(--env "GPU=Y")
run_args+=(--device=/dev/kvm)
run_args+=(--volume "${PWD}/${image_file}":"/boot.${type}")
run_args+=(docker.io/qemux/qemu-docker)
# Run the VM and open the browser to connect
(sleep 30 && xdg-open http://localhost:"$port") &
podman run "${run_args[@]}"
# Run a virtual machine from a QCOW2 image
[group('Run Virtal Machine')]
run-vm-qcow2 $target_image=("localhost/" + image_name) $tag=default_tag: && (_run-vm target_image tag "qcow2" "image.toml")
# Run a virtual machine from a RAW image
[group('Run Virtal Machine')]
run-vm-raw $target_image=("localhost/" + image_name) $tag=default_tag: && (_run-vm target_image tag "raw" "image.toml")
# Run a virtual machine from an ISO
[group('Run Virtal Machine')]
run-vm-iso $target_image=("localhost/" + image_name) $tag=default_tag: && (_run-vm target_image tag "iso" "iso.toml")
# Run a virtual machine using systemd-vmspawn
[group('Run Virtal Machine')]
spawn-vm rebuild="0" type="qcow2" ram="6G":
#!/usr/bin/env bash
set -euo pipefail
[ "{{ rebuild }}" -eq 1 ] && echo "Rebuilding the ISO" && just build-vm {{ rebuild }} {{ type }}
systemd-vmspawn \
-M "bootc-image" \
--console=gui \
--cpus=2 \
--ram=$(echo {{ ram }}| /usr/bin/numfmt --from=iec) \
--network-user-mode \
--vsock=false --pass-ssh-key=false \
-i ./output/**/*.{{ type }}
# Runs shell check on all Bash scripts
lint:
/usr/bin/find . -iname "*.sh" -type f -exec shellcheck "{}" ';'
# Runs shfmt on all Bash scripts
format:
/usr/bin/find . -iname "*.sh" -type f -exec shfmt --write "{}" ';'

201
LICENSE Normal file
View file

@ -0,0 +1,201 @@
Apache License
Version 2.0, January 2004
http://www.apache.org/licenses/
TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION
1. Definitions.
"License" shall mean the terms and conditions for use, reproduction,
and distribution as defined by Sections 1 through 9 of this document.
"Licensor" shall mean the copyright owner or entity authorized by
the copyright owner that is granting the License.
"Legal Entity" shall mean the union of the acting entity and all
other entities that control, are controlled by, or are under common
control with that entity. For the purposes of this definition,
"control" means (i) the power, direct or indirect, to cause the
direction or management of such entity, whether by contract or
otherwise, or (ii) ownership of fifty percent (50%) or more of the
outstanding shares, or (iii) beneficial ownership of such entity.
"You" (or "Your") shall mean an individual or Legal Entity
exercising permissions granted by this License.
"Source" form shall mean the preferred form for making modifications,
including but not limited to software source code, documentation
source, and configuration files.
"Object" form shall mean any form resulting from mechanical
transformation or translation of a Source form, including but
not limited to compiled object code, generated documentation,
and conversions to other media types.
"Work" shall mean the work of authorship, whether in Source or
Object form, made available under the License, as indicated by a
copyright notice that is included in or attached to the work
(an example is provided in the Appendix below).
"Derivative Works" shall mean any work, whether in Source or Object
form, that is based on (or derived from) the Work and for which the
editorial revisions, annotations, elaborations, or other modifications
represent, as a whole, an original work of authorship. For the purposes
of this License, Derivative Works shall not include works that remain
separable from, or merely link (or bind by name) to the interfaces of,
the Work and Derivative Works thereof.
"Contribution" shall mean any work of authorship, including
the original version of the Work and any modifications or additions
to that Work or Derivative Works thereof, that is intentionally
submitted to Licensor for inclusion in the Work by the copyright owner
or by an individual or Legal Entity authorized to submit on behalf of
the copyright owner. For the purposes of this definition, "submitted"
means any form of electronic, verbal, or written communication sent
to the Licensor or its representatives, including but not limited to
communication on electronic mailing lists, source code control systems,
and issue tracking systems that are managed by, or on behalf of, the
Licensor for the purpose of discussing and improving the Work, but
excluding communication that is conspicuously marked or otherwise
designated in writing by the copyright owner as "Not a Contribution."
"Contributor" shall mean Licensor and any individual or Legal Entity
on behalf of whom a Contribution has been received by Licensor and
subsequently incorporated within the Work.
2. Grant of Copyright License. Subject to the terms and conditions of
this License, each Contributor hereby grants to You a perpetual,
worldwide, non-exclusive, no-charge, royalty-free, irrevocable
copyright license to reproduce, prepare Derivative Works of,
publicly display, publicly perform, sublicense, and distribute the
Work and such Derivative Works in Source or Object form.
3. Grant of Patent License. Subject to the terms and conditions of
this License, each Contributor hereby grants to You a perpetual,
worldwide, non-exclusive, no-charge, royalty-free, irrevocable
(except as stated in this section) patent license to make, have made,
use, offer to sell, sell, import, and otherwise transfer the Work,
where such license applies only to those patent claims licensable
by such Contributor that are necessarily infringed by their
Contribution(s) alone or by combination of their Contribution(s)
with the Work to which such Contribution(s) was submitted. If You
institute patent litigation against any entity (including a
cross-claim or counterclaim in a lawsuit) alleging that the Work
or a Contribution incorporated within the Work constitutes direct
or contributory patent infringement, then any patent licenses
granted to You under this License for that Work shall terminate
as of the date such litigation is filed.
4. Redistribution. You may reproduce and distribute copies of the
Work or Derivative Works thereof in any medium, with or without
modifications, and in Source or Object form, provided that You
meet the following conditions:
(a) You must give any other recipients of the Work or
Derivative Works a copy of this License; and
(b) You must cause any modified files to carry prominent notices
stating that You changed the files; and
(c) You must retain, in the Source form of any Derivative Works
that You distribute, all copyright, patent, trademark, and
attribution notices from the Source form of the Work,
excluding those notices that do not pertain to any part of
the Derivative Works; and
(d) If the Work includes a "NOTICE" text file as part of its
distribution, then any Derivative Works that You distribute must
include a readable copy of the attribution notices contained
within such NOTICE file, excluding those notices that do not
pertain to any part of the Derivative Works, in at least one
of the following places: within a NOTICE text file distributed
as part of the Derivative Works; within the Source form or
documentation, if provided along with the Derivative Works; or,
within a display generated by the Derivative Works, if and
wherever such third-party notices normally appear. The contents
of the NOTICE file are for informational purposes only and
do not modify the License. You may add Your own attribution
notices within Derivative Works that You distribute, alongside
or as an addendum to the NOTICE text from the Work, provided
that such additional attribution notices cannot be construed
as modifying the License.
You may add Your own copyright statement to Your modifications and
may provide additional or different license terms and conditions
for use, reproduction, or distribution of Your modifications, or
for any such Derivative Works as a whole, provided Your use,
reproduction, and distribution of the Work otherwise complies with
the conditions stated in this License.
5. Submission of Contributions. Unless You explicitly state otherwise,
any Contribution intentionally submitted for inclusion in the Work
by You to the Licensor shall be under the terms and conditions of
this License, without any additional terms or conditions.
Notwithstanding the above, nothing herein shall supersede or modify
the terms of any separate license agreement you may have executed
with Licensor regarding such Contributions.
6. Trademarks. This License does not grant permission to use the trade
names, trademarks, service marks, or product names of the Licensor,
except as required for reasonable and customary use in describing the
origin of the Work and reproducing the content of the NOTICE file.
7. Disclaimer of Warranty. Unless required by applicable law or
agreed to in writing, Licensor provides the Work (and each
Contributor provides its Contributions) on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or
implied, including, without limitation, any warranties or conditions
of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A
PARTICULAR PURPOSE. You are solely responsible for determining the
appropriateness of using or redistributing the Work and assume any
risks associated with Your exercise of permissions under this License.
8. Limitation of Liability. In no event and under no legal theory,
whether in tort (including negligence), contract, or otherwise,
unless required by applicable law (such as deliberate and grossly
negligent acts) or agreed to in writing, shall any Contributor be
liable to You for damages, including any direct, indirect, special,
incidental, or consequential damages of any character arising as a
result of this License or out of the use or inability to use the
Work (including but not limited to damages for loss of goodwill,
work stoppage, computer failure or malfunction, or any and all
other commercial damages or losses), even if such Contributor
has been advised of the possibility of such damages.
9. Accepting Warranty or Additional Liability. While redistributing
the Work or Derivative Works thereof, You may choose to offer,
and charge a fee for, acceptance of support, warranty, indemnity,
or other liability obligations and/or rights consistent with this
License. However, in accepting such obligations, You may act only
on Your own behalf and on Your sole responsibility, not on behalf
of any other Contributor, and only if You agree to indemnify,
defend, and hold each Contributor harmless for any liability
incurred by, or claims asserted against, such Contributor by reason
of your accepting any such warranty or additional liability.
END OF TERMS AND CONDITIONS
APPENDIX: How to apply the Apache License to your work.
To apply the Apache License to your work, attach the following
boilerplate notice, with the fields enclosed by brackets "[]"
replaced with your own identifying information. (Don't include
the brackets!) The text should be enclosed in the appropriate
comment syntax for the file format. We also recommend that a
file or class name and description of purpose be included on the
same "printed page" as the copyright notice for easier
identification within third-party archives.
Copyright [yyyy] [name of copyright owner]
Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at
http://www.apache.org/licenses/LICENSE-2.0
Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.

274
README.md Normal file
View file

@ -0,0 +1,274 @@
# image-template
# Purpose
This repository is meant to be a template for building your own custom Universal Blue image. This template is the recommended way to make customizations to any image published by the Universal Blue Project:
- [Aurora](https://getaurora.dev/)
- [Bazzite](https://bazzite.gg/)
- [Bluefin](https://projectbluefin.io/)
- [uCore](https://projectucore.io/)
- [main](https://github.com/ublue-os/main/)
- [hwe](https://github.com/ublue-os/hwe/)
or any other base image if you want to start from scratch:
- Fedora: `quay.io/fedora/fedora-bootc:41`
- CentOS Stream 9: `quay.io/centos-bootc/centos-bootc:stream9`
- CentOS Stream 10 (in development): `quay.io/centos-bootc/centos-bootc:stream10`
This template includes a Containerfile and a Github workflow for building the container image, signing, and proper metadata to be listed on [artifacthub](https://artifacthub.io/). As soon as the workflow is enabled in your repository, it will build the container image and push it to the Github Container Registry.
# Prerequisites
Working knowledge in the following topics:
- Containers
- https://www.youtube.com/watch?v=SnSH8Ht3MIc
- https://www.mankier.com/5/Containerfile
- bootc
- https://bootc-dev.github.io/bootc/
- Fedora Silverblue (and other Fedora Atomic variants)
- https://docs.fedoraproject.org/en-US/fedora-silverblue/
- Github Workflows
- https://docs.github.com/en/actions/using-workflows
# How to Use
## Template
Select `Use this Template` and create a new repository from it. To enable the workflows, you may need to go the `Actions` tab of the new repository and click to enable workflows.
## Containerfile
This file defines the operations used to customize the selected image. It contains examples of possible modifications, including how to:
- change the upstream from which the custom image is derived
- add additional RPM packages
- add binaries as a layer from other images
## Building an ISO
This template provides an out of the box workflow for getting an ISO image for your custom OCI image which can be used to directly install onto your machines.
This template provides a way to upload the ISO that is generated from the workflow to a S3 bucket or it will be available as an artifact from the job. To upload to S3 we use a tool called [rclone](https://rclone.org/) which is able to use [many S3 providers](https://rclone.org/s3/). For more details on how to configure this see the details [below](#build-isoyml).
### Justfile Documentation
This `Justfile` contains various commands and configurations for building and managing container images and virtual machine images using Podman and other utilities.
#### Environment Variables
- `repo_organization`: The GitHub repository owner (default: "yourname").
- `image_name`: The name of the image (default: "yourimage").
- `centos_version`: The CentOS version (default: "stream10").
- `fedora_version`: The Fedora version (default: "41").
- `default_tag`: The default tag for the image (default: "latest").
- `bib_image`: The Bootc Image Builder (BIB) image (default: "quay.io/centos-bootc/bootc-image-builder:latest").
#### Aliases
- `build-vm`: Alias for `build-qcow2`.
- `rebuild-vm`: Alias for `rebuild-qcow2`.
- `run-vm`: Alias for `run-vm-qcow2`.
#### Commands
###### `check`
Checks the syntax of all `.just` files and the `Justfile`.
###### `fix`
Fixes the syntax of all `.just` files and the `Justfile`.
###### `clean`
Cleans the repository by removing build artifacts.
##### Build Commands
###### `build`
Builds a container image using Podman.
```bash
just build $target_image $tag $dx $hwe $gdx
```
Arguments:
- `$target_image`: The tag you want to apply to the image (default: aurora).
- `$tag`: The tag for the image (default: lts).
- `$dx`: Enable DX (default: "0").
- `$hwe`: Enable HWE (default: "0").
- `$gdx`: Enable GDX (default: "0").
##### Building Virtual Machines and ISOs
###### `build-qcow2`
Builds a QCOW2 virtual machine image.
```bash
just build-qcow2 $target_image $tag
```
###### `build-raw`
Builds a RAW virtual machine image.
```bash
just build-raw $target_image $tag
```
###### `build-iso`
Builds an ISO virtual machine image.
```bash
just build-iso $target_image $tag
```
###### `rebuild-qcow2`
Rebuilds a QCOW2 virtual machine image.
```bash
just rebuild-qcow2 $target_image $tag
```
###### `rebuild-raw`
Rebuilds a RAW virtual machine image.
```bash
just rebuild-raw $target_image $tag
```
###### `rebuild-iso`
Rebuilds an ISO virtual machine image.
```bash
just rebuild-iso $target_image $tag
```
##### Run Virtual Machines
###### `run-vm-qcow2`
Runs a virtual machine from a QCOW2 image.
```bash
just run-vm-qcow2 $target_image $tag
```
###### `run-vm-raw`
Runs a virtual machine from a RAW image.
```bash
just run-vm-raw $target_image $tag
```
###### `run-vm-iso`
Runs a virtual machine from an ISO.
```bash
just run-vm-iso $target_image $tag
```
###### `spawn-vm`
Runs a virtual machine using systemd-vmspawn.
```bash
just spawn-vm rebuild="0" type="qcow2" ram="6G"
```
##### Lint and Format
###### `lint`
Runs shell check on all Bash scripts.
###### `format`
Runs shfmt on all Bash scripts.
## Workflows
### build.yml
This workflow creates your custom OCI image and publishes it to the Github Container Registry (GHCR). By default, the image name will match the Github repository name.
### build-iso.yml
This workflow creates an ISO from your OCI image by utilizing the [bootc-image-builder](https://osbuild.org/docs/bootc/) to generate an ISO. In order to use this workflow you must complete the following steps:
- Modify `iso.toml` to point to your custom image before generating an ISO.
- If you changed your image name from the default in `build.yml` then in the `build-iso.yml` file edit the `IMAGE_REGISTRY` and `DEFAULT_TAG` environment variables with the correct values. If you did not make changes, skip this step.
- Finally, if you want to upload your ISOs to S3 then you will need to add your S3 configuration to the repository's Action secrets. This can be found by going to your repository settings, under `Secrets and Variables` -> `Actions`. You will need to add the following
- `S3_PROVIDER` - Must match one of the values from the [supported list](https://rclone.org/s3/)
- `S3_BUCKET_NAME` - Your unique bucket name
- `S3_ACCESS_KEY_ID` - It is recommended that you make a separate key just for this workflow
- `S3_SECRET_ACCESS_KEY` - See above.
- `S3_REGION` - The region your bucket lives in. If you do not know then set this value to `auto`.
- `S3_ENDPOINT` - This value will be specific to the bucket as well.
Once the workflow is done, you'll find it either in your S3 bucket or as part of the summary under `Artifacts` after the workflow is completed.
#### Container Signing
Container signing is important for end-user security and is enabled on all Universal Blue images. It is recommended you set this up, and by default the image builds *will fail* if you don't.
This provides users a method of verifying the image.
1. Install the [cosign CLI tool](https://edu.chainguard.dev/open-source/sigstore/cosign/how-to-install-cosign/#installing-cosign-with-the-cosign-binary)
2. Run inside your repo folder:
```bash
cosign generate-key-pair
```
- Do NOT put in a password when it asks you to, just press enter. The signing key will be used in GitHub Actions and will not work if it is encrypted.
> [!WARNING]
> Be careful to *never* accidentally commit `cosign.key` into your git repo.
3. Add the private key to GitHub
- This can also be done manually. Go to your repository settings, under `Secrets and Variables` -> `Actions`
![image](https://user-images.githubusercontent.com/1264109/216735595-0ecf1b66-b9ee-439e-87d7-c8cc43c2110a.png)
Add a new secret and name it `SIGNING_SECRET`, then paste the contents of `cosign.key` into the secret and save it. Make sure it's the .key file and not the .pub file. Once done, it should look like this:
![image](https://user-images.githubusercontent.com/1264109/216735690-2d19271f-cee2-45ac-a039-23e6a4c16b34.png)
- (CLI instructions) If you have the `github-cli` installed, run:
```bash
gh secret set SIGNING_SECRET < cosign.key
```
4. Commit the `cosign.pub` file to the root of your git repository.
# Community
- [**bootc discussion forums**](https://github.com/bootc-dev/bootc/discussions) - Nothing in this template is ublue specific, the upstream bootc project has a discussions forum where custom image builders can hang out and ask questions.
## Artifacthub
This template comes with the necessary tooling to index your image on [artifacthub.io](https://artifacthub.io), use the `artifacthub-repo.yml` file at the root to verify yourself as the publisher. This is important to you for a few reasons:
- The value of artifacthub is it's one place for people to index their custom images, and since we depend on each other to learn, it helps grow the community.
- You get to see your pet project listed with the other cool projects in Cloud Native.
- Since the site puts your README front and center, it's a good way to learn how to write a good README, learn some marketing, finding your audience, etc.
[Discussion thread](https://universal-blue.discourse.group/t/listing-your-custom-image-on-artifacthub/6446)
## Community Examples
- [m2os](https://github.com/m2giles/m2os)
- [bos](https://github.com/bsherman/bos)
- [homer](https://github.com/bketelsen/homer/)

8
artifacthub-repo.yml Normal file
View file

@ -0,0 +1,8 @@
# This file is completely optional, but if you want to index your image on https://artifacthub.io/ you can
# Sign up and add the Repository ID to the right field. Owners fields are optional.
# Examples: https://artifacthub.io/packages/search?ts_query_web=ublue&sort=relevance&page=1
repositoryID: my-custom-id-here # Fill in with your own credentials
owners: # (optional, used to claim repository ownership)
- name: My Name
email: my_email@email.com

25
build_files/build.sh Executable file
View file

@ -0,0 +1,25 @@
#!/bin/bash
set -ouex pipefail
### Install packages
# Packages can be installed from any enabled yum repo on the image.
# RPMfusion repos are available by default in ublue main images
# List of rpmfusion packages can be found here:
# https://mirrors.rpmfusion.org/mirrorlist?path=free/fedora/updates/39/x86_64/repoview/index.html&protocol=https&redirect=1
# Basic packages
dnf5 install -y fish htop podman-compose podman-docker tmux vim systemd-networkd
dnf5 remove -y NetworkManager
# Incus related packages
dnf5 install -y 'dnf5-command(copr)'
dnf5 -y copr enable rcallicotte/incus
dnf5 install -y distrobox edk2-ovmf incus qemu-system-x86 swtpm zstd
#### Enable services
systemctl enable podman.socket
systemctl enable incus-startup
systemctl enable systemd-networkd

3
image.toml Normal file
View file

@ -0,0 +1,3 @@
[[customizations.filesystem]]
mountpoint = "/"
minsize = "20 GiB"

19
iso.toml Normal file
View file

@ -0,0 +1,19 @@
[customizations.installer.kickstart]
contents = """
%post
bootc switch --mutate-in-place --transport registry ghcr.io/yourrepo/yourimage:latest
%end
"""
[customizations.installer.modules]
enable = [
"org.fedoraproject.Anaconda.Modules.Storage"
]
disable = [
"org.fedoraproject.Anaconda.Modules.Network",
"org.fedoraproject.Anaconda.Modules.Security",
"org.fedoraproject.Anaconda.Modules.Services",
"org.fedoraproject.Anaconda.Modules.Users",
"org.fedoraproject.Anaconda.Modules.Subscription",
"org.fedoraproject.Anaconda.Modules.Timezone"
]