diff --git a/.github/workflows/reusable-build.yaml b/.github/workflows/reusable-build.yaml index 51b90b2..de0ea25 100644 --- a/.github/workflows/reusable-build.yaml +++ b/.github/workflows/reusable-build.yaml @@ -168,6 +168,7 @@ jobs: SOURCE_ORG=${{ env.SOURCE_ORG }} SOURCE_IMAGE=${{ env.SOURCE_IMAGE }} FEDORA_MAJOR_VERSION=${{ matrix.fedora_version }} + RPMFUSION_MIRROR=${{ vars.RPMFUSION_MIRROR }} labels: ${{ steps.meta.outputs.labels }} platforms: "linux/aarch64" oci: false diff --git a/Containerfile b/Containerfile index 0b0e5c7..2370809 100644 --- a/Containerfile +++ b/Containerfile @@ -8,8 +8,11 @@ FROM ${BASE_IMAGE}:${FEDORA_MAJOR_VERSION} ARG IMAGE_NAME="${IMAGE_NAME:-silverblue}" ARG FEDORA_MAJOR_VERSION="${FEDORA_MAJOR_VERSION:-40}" +ARG RPMFUSION_MIRROR="" RUN mkdir -p /var/lib/alternatives && \ + /tmp/install.sh && \ + /tmp/post-install.sh && \ mv /var/lib/alternatives /staged-alternatives && \ rm -rf /tmp/* /var/* && \ ostree container commit && \ diff --git a/github-release-install.sh b/github-release-install.sh new file mode 100755 index 0000000..aa9243e --- /dev/null +++ b/github-release-install.sh @@ -0,0 +1,60 @@ +#!/bin/bash +# +# A script to install an RPM from the latest Github release for a project. +# +# ORG_PROJ is the pair of URL components for organization/projectName in Github URL +# example: https://github.com/wez/wezterm/releases +# ORG_PROJ would be "wez/wezterm" +# +# ARCH_FILTER is used to select the specific RPM. Typically this can just be the arch +# such as 'x86_64' but sometimes a specific filter is required when multiple match. +# example: wezterm builds RPMs for different distros so we must be more specific. +# ARCH_FILTER of "fedora37.x86_64" gets the x86_64 RPM build for fedora37 + + +ORG_PROJ=${1} +ARCH_FILTER=${2} +LATEST=${3} + +usage() { + echo "$0 ORG_PROJ ARCH_FILTER" + echo " ORG_PROJ - organization/projectname" + echo " ARCH_FILTER - optional extra filter to further limit rpm selection" + echo " LATEST - optional tag override for latest release (eg, nightly-dev)" + +} + +if [ -z ${ORG_PROJ} ]; then + usage + exit 1 +fi + +if [ -z ${ARCH_FILTER} ]; then + usage + exit 2 +fi + +if [ -z ${LATEST} ]; then + RELTAG="latest" +else + RELTAG="tags/${LATEST}" +fi + +set -ouex pipefail + +API_JSON=$(mktemp /tmp/api-XXXXXXXX.json) +API="https://api.github.com/repos/${ORG_PROJ}/releases/${RELTAG}" + +# retry up to 5 times with 5 second delays for any error included HTTP 404 etc +curl --fail --retry 5 --retry-delay 5 --retry-all-errors -sL ${API} -o ${API_JSON} +RPM_URLS=$(cat ${API_JSON} \ + | jq \ + -r \ + --arg arch_filter "${ARCH_FILTER}" \ + '.assets | sort_by(.created_at) | reverse | .[] | select(.name|test($arch_filter)) | select (.name|test("rpm$")) | .browser_download_url') +for URL in ${RPM_URLS}; do + # WARNING: in case of multiple matches, this only installs the first matched release + echo "execute: rpm-ostree install \"${URL}\"" + rpm-ostree install "${URL}" + break +done diff --git a/install.sh b/install.sh new file mode 100755 index 0000000..afe644b --- /dev/null +++ b/install.sh @@ -0,0 +1,52 @@ +#!/bin/sh + +set -ouex pipefail + +RELEASE="$(rpm -E %fedora)" + +RPMFUSION_MIRROR_RPMS="https://mirrors.rpmfusion.org" +if [ -n "${RPMFUSION_MIRROR}" ]; then + RPMFUSION_MIRROR_RPMS=${RPMFUSION_MIRROR} +fi + +curl -Lo /tmp/rpms/rpmfusion-free-release-${RELEASE}.noarch.rpm ${RPMFUSION_MIRROR_RPMS}/free/fedora/rpmfusion-free-release-${RELEASE}.noarch.rpm +curl -Lo /tmp/rpms/rpmfusion-nonfree-release-${RELEASE}.noarch.rpm ${RPMFUSION_MIRROR_RPMS}/nonfree/fedora/rpmfusion-nonfree-release-${RELEASE}.noarch.rpm + +curl -Lo /etc/yum.repos.d/_copr_ublue-os_staging.repo https://copr.fedorainfracloud.org/coprs/ublue-os/staging/repo/fedora-${RELEASE}/ublue-os-staging-fedora-${RELEASE}.repo +curl -Lo /etc/yum.repos.d/_copr_kylegospo_oversteer.repo https://copr.fedorainfracloud.org/coprs/kylegospo/oversteer/repo/fedora-${RELEASE}/kylegospo-oversteer-fedora-${RELEASE}.repo + +rpm-ostree install \ + /tmp/rpms/*.rpm \ + fedora-repos-archive + +if [[ "${FEDORA_MAJOR_VERSION}" -ge 39 ]]; then + # note: this is done before single mirror hack to ensure this persists in image and is not reset + echo "Enable rpmfusion-(non)free-updates-testing with low priority for Fedora ${FEDORA_MAJOR_VERSION}" + sed -i '0,/enabled=0/{s/enabled=0/enabled=1\npriority=110/}' /etc/yum.repos.d/rpmfusion-*-updates-testing.repo +fi + +# after F41 launches, bump to 42 +if [[ "${FEDORA_MAJOR_VERSION}" -ge 41 ]]; then + # note: this is done before single mirror hack to ensure this persists in image and is not reset + # pre-release rpmfusion is in a different location + sed -i "s%free/fedora/releases%free/fedora/development%" /etc/yum.repos.d/rpmfusion-*.repo +fi + +if [ -n "${RPMFUSION_MIRROR}" ]; then + # force use of single rpmfusion mirror + echo "Using single rpmfusion mirror: ${RPMFUSION_MIRROR}" + sed -i.bak "s%^metalink=%#metalink=%" /etc/yum.repos.d/rpmfusion-*.repo + sed -i "s%^#baseurl=http://download1.rpmfusion.org%baseurl=${RPMFUSION_MIRROR}%" /etc/yum.repos.d/rpmfusion-*.repo +fi + +# run common packages script +/tmp/packages.sh + +## install packages direct from github +/tmp/github-release-install.sh sigstore/cosign aarch64 + +if [ -n "${RPMFUSION_MIRROR}" ]; then + # reset forced use of single rpmfusion mirror + echo "Revert from single rpmfusion mirror: ${RPMFUSION_MIRROR}" + rename -v .repo.bak .repo /etc/yum.repos.d/rpmfusion-*repo.bak +fi diff --git a/packages.json b/packages.json new file mode 100644 index 0000000..f8d0659 --- /dev/null +++ b/packages.json @@ -0,0 +1,92 @@ +{ + "all": { + "include": { + "all": [ + "alsa-firmware", + "android-udev-rules", + "apr", + "apr-util", + "distrobox", + "ffmpeg", + "ffmpeg-libs", + "ffmpegthumbnailer", + "fzf", + "google-noto-sans-balinese-fonts", + "google-noto-sans-cjk-fonts", + "google-noto-sans-javanese-fonts", + "google-noto-sans-sundanese-fonts", + "grub2-tools-extra", + "heif-pixbuf-loader", + "htop", + "just", + "kernel-tools", + "libheif-freeworld", + "libheif-tools", + "libratbag-ratbagd", + "lshw", + "net-tools", + "nvme-cli", + "nvtop", + "openrgb-udev-rules", + "openssl", + "oversteer-udev", + "pam-u2f", + "pam_yubico", + "pamu2fcfg", + "pipewire-codec-aptx", + "powerstat", + "smartmontools", + "solaar-udev", + "symlinks", + "tcpdump", + "tmux", + "traceroute", + "vim", + "wireguard-tools", + "zstd" + ], + "silverblue": [ + "adw-gtk3-theme", + "gnome-epub-thumbnailer", + "gnome-tweaks", + "gvfs-nfs" + ], + "kinoite": [ + "icoutils", + "kate", + "kcron", + "kio-admin", + "ksshaskpass" + ] + }, + "exclude": { + "all": [ + "ffmpeg-free", + "google-noto-sans-cjk-vf-fonts", + "libavcodec-free", + "libavdevice-free", + "libavfilter-free", + "libavformat-free", + "libavutil-free", + "libpostproc-free", + "libswresample-free", + "libswscale-free" + ], + "kinoite": [ + "ffmpegthumbnailer", + "plasma-discover-rpm-ostree" + ] + } + }, + "40": { + "include": { + "all": [], + "kinoite": [] + }, + "exclude": { + "all": [ + "default-fonts-cjk-sans" + ] + } + } +} diff --git a/packages.sh b/packages.sh new file mode 100755 index 0000000..0c444c3 --- /dev/null +++ b/packages.sh @@ -0,0 +1,53 @@ +#!/bin/sh + +set -ouex pipefail + +RELEASE="$(rpm -E %fedora)" + +# build list of all packages requested for inclusion +INCLUDED_PACKAGES=($(jq -r "[(.all.include | (.all, select(.\"$IMAGE_NAME\" != null).\"$IMAGE_NAME\")[]), \ + (select(.\"$FEDORA_MAJOR_VERSION\" != null).\"$FEDORA_MAJOR_VERSION\".include | (.all, select(.\"$IMAGE_NAME\" != null).\"$IMAGE_NAME\")[])] \ + | sort | unique[]" /tmp/packages.json)) + +# build list of all packages requested for exclusion +EXCLUDED_PACKAGES=($(jq -r "[(.all.exclude | (.all, select(.\"$IMAGE_NAME\" != null).\"$IMAGE_NAME\")[]), \ + (select(.\"$FEDORA_MAJOR_VERSION\" != null).\"$FEDORA_MAJOR_VERSION\".exclude | (.all, select(.\"$IMAGE_NAME\" != null).\"$IMAGE_NAME\")[])] \ + | sort | unique[]" /tmp/packages.json)) + + +# ensure exclusion list only contains packages already present on image +if [[ "${#EXCLUDED_PACKAGES[@]}" -gt 0 ]]; then + EXCLUDED_PACKAGES=($(rpm -qa --queryformat='%{NAME} ' ${EXCLUDED_PACKAGES[@]})) +fi + +# simple case to install where no packages need excluding +if [[ "${#INCLUDED_PACKAGES[@]}" -gt 0 && "${#EXCLUDED_PACKAGES[@]}" -eq 0 ]]; then + rpm-ostree install \ + ${INCLUDED_PACKAGES[@]} + +# install/excluded packages both at same time +elif [[ "${#INCLUDED_PACKAGES[@]}" -gt 0 && "${#EXCLUDED_PACKAGES[@]}" -gt 0 ]]; then + rpm-ostree override remove \ + ${EXCLUDED_PACKAGES[@]} \ + $(printf -- "--install=%s " ${INCLUDED_PACKAGES[@]}) + +else + echo "No packages to install." + +fi + +# check if any excluded packages are still present +# (this can happen if an included package pulls in a dependency) +EXCLUDED_PACKAGES=($(jq -r "[(.all.exclude | (.all, select(.\"$IMAGE_NAME\" != null).\"$IMAGE_NAME\")[]), \ + (select(.\"$FEDORA_MAJOR_VERSION\" != null).\"$FEDORA_MAJOR_VERSION\".exclude | (.all, select(.\"$IMAGE_NAME\" != null).\"$IMAGE_NAME\")[])] \ + | sort | unique[]" /tmp/packages.json)) + +if [[ "${#EXCLUDED_PACKAGES[@]}" -gt 0 ]]; then + EXCLUDED_PACKAGES=($(rpm -qa --queryformat='%{NAME} ' ${EXCLUDED_PACKAGES[@]})) +fi + +# remove any excluded packages which are still present on image +if [[ "${#EXCLUDED_PACKAGES[@]}" -gt 0 ]]; then + rpm-ostree override remove \ + ${EXCLUDED_PACKAGES[@]} +fi diff --git a/post-install.sh b/post-install.sh new file mode 100755 index 0000000..c783fcf --- /dev/null +++ b/post-install.sh @@ -0,0 +1,19 @@ +#!/bin/sh + +set -ouex pipefail + +if [[ "$IMAGE_NAME" == "base" ]]; then + systemctl enable getty@tty1 +fi + +systemctl enable rpm-ostreed-automatic.timer +systemctl enable flatpak-system-update.timer + +systemctl --global enable flatpak-user-update.timer + +cp /usr/share/ublue-os/update-services/etc/rpm-ostreed.conf /etc/rpm-ostreed.conf + +ln -s "/usr/share/fonts/google-noto-sans-cjk-fonts" "/usr/share/fonts/noto-cjk" + +rm -f /etc/yum.repos.d/_copr_ublue-os_staging.repo +rm -f /etc/yum.repos.d/_copr_kylegospo_oversteer.repo