diff --git a/.github/workflows/build.yml b/.github/workflows/build.yml index 2c61e48..09ba95c 100644 --- a/.github/workflows/build.yml +++ b/.github/workflows/build.yml @@ -31,7 +31,7 @@ jobs: steps: # the build is fully handled by the reusable github action - name: Build Custom Image - uses: blue-build/github-action@v1.10 + uses: blue-build/github-action@v1.8 with: recipe: ${{ matrix.recipe }} cosign_private_key: ${{ secrets.SIGNING_SECRET }} diff --git a/.gitignore b/.gitignore index 8703795..8199d78 100644 --- a/.gitignore +++ b/.gitignore @@ -1,3 +1,4 @@ cosign.key cosign.private /Containerfile +/.bluebuild-scripts_* diff --git a/README.md b/README.md index 351f8a0..315f19f 100644 --- a/README.md +++ b/README.md @@ -1,43 +1,37 @@ -# randofin-os   [![bluebuild build badge](https://github.com/randogoth/randofin-os/actions/workflows/build.yml/badge.svg)](https://github.com/randogoth/randofin-os/actions/workflows/build.yml) +# Deinonyxus   [![bluebuild build badge](https://github.com/randogoth/deinonyxus/actions/workflows/build.yml/badge.svg)](https://github.com/randogoth/deinonyxus/actions/workflows/build.yml) -See the [BlueBuild docs](https://blue-build.org/how-to/setup/) for quick setup instructions for setting up your own repository based on this template. +*Deinonyxus* is a personal spin of the UBlue Bluefin DX image with the 🍦[Lix](https://lix.systems/) flavored Nix package manager baked in. -After setup, it is recommended you update this README to describe your custom image. +## What’s inside +- Base: `ghcr.io/ublue-os/bluefin-dx:latest` without Cockpit, Docker, Firefox, VS Code +- System packages added: `syncthing`, `uv`, `vscodium`, `waydroid`; +- System flatpaks added: Telegram Desktop, Waterfox browser -## Installation +## Just Recipes +- `upgrade-nix`: upgrades to the latest version of Lix via the user profile. Replaces `nix upgrade-nix` which does not work with an immutable lowerdir `/nix/store` folder +- `install-nix-software-center`: installs a graphical app store for Nix packages -> [!WARNING] -> [This is an experimental feature](https://www.fedoraproject.org/wiki/Changes/OstreeNativeContainerStable), try at your own discretion. - -To rebase an existing atomic Fedora installation to the latest build: - -- First rebase to the unsigned image, to get the proper signing keys and policies installed: - ``` - rpm-ostree rebase ostree-unverified-registry:ghcr.io/randogoth/randofin-os:latest - ``` -- Reboot to complete the rebase: - ``` - systemctl reboot - ``` -- Then rebase to the signed image, like so: - ``` - rpm-ostree rebase ostree-image-signed:docker://ghcr.io/randogoth/randofin-os:latest - ``` -- Reboot again to complete the installation - ``` - systemctl reboot - ``` - -The `latest` tag will automatically point to the latest build. That build will still always use the Fedora version specified in `recipe.yml`, so you won't get accidentally updated to the next major version. - -## ISO - -If build on Fedora Atomic, you can generate an offline ISO with the instructions available [here](https://blue-build.org/learn/universal-blue/#fresh-install-from-an-iso). These ISOs cannot unfortunately be distributed on GitHub for free due to large sizes, so for public projects something else has to be used for hosting. - -## Verification - -These images are signed with [Sigstore](https://www.sigstore.dev/)'s [cosign](https://github.com/sigstore/cosign). You can verify the signature by downloading the `cosign.pub` file from this repo and running the following command: +## Install / Rebase ```bash -cosign verify --key cosign.pub ghcr.io/randogoth/randofin-os +# First pull unsigned to get signing policy +rpm-ostree rebase ostree-unverified-registry:ghcr.io/randogoth/deinonyxus:latest +systemctl reboot + +# Then move to the signed image +rpm-ostree rebase ostree-image-signed:docker://ghcr.io/randogoth/deinonyxus:latest +systemctl reboot +``` + +The `latest` tag always tracks the latest build for the Fedora base set in `recipes/recipe.yml`. + +## Building locally +```bash +bluebuild build +``` + +## Signature verification +Images are signed with Sigstore/cosign. Verify with the repo's `cosign.pub`: +```bash +cosign verify --key cosign.pub ghcr.io/randogoth/deinonyxus ``` diff --git a/files/justfiles/nixpkgs.just b/files/justfiles/nixpkgs.just new file mode 100644 index 0000000..9ba6977 --- /dev/null +++ b/files/justfiles/nixpkgs.just @@ -0,0 +1,7 @@ +upgrade-nix: + echo 'Installing latest Lix package' + nix profile install nixpkgs#lix && nix upgrade-nix + +install-nix-software-center: + echo 'Installing Nix Software Center' + nix profile install github:snowfallorg/nix-software-center \ No newline at end of file diff --git a/files/scripts/example.sh b/files/scripts/example.sh deleted file mode 100644 index 1cded87..0000000 --- a/files/scripts/example.sh +++ /dev/null @@ -1,6 +0,0 @@ -#!/usr/bin/env bash - -# Tell this script to exit if there are any errors. -# You should have this in every custom script, to ensure that your completed -# builds actually ran successfully without any errors! -set -oue pipefail \ No newline at end of file diff --git a/files/scripts/install-lix.sh b/files/scripts/install-lix.sh new file mode 100755 index 0000000..08d3cba --- /dev/null +++ b/files/scripts/install-lix.sh @@ -0,0 +1,48 @@ +#!/usr/bin/env bash +set -euo pipefail + +# === INSTALL LIX FROM RPM === + +rpm_url="https://nix-community.github.io/nix-installers/lix/x86_64/lix-multi-user-2.91.1.rpm" + +install -d /usr/share/nix-store /var/lib/nix-store /nix /etc/nix + +# Avoid systemd calls during RPM %post in the image build environment. +export SYSTEMD_OFFLINE=1 + +# Install the RPM; allow missing GPG key since we fetch directly by URL. +dnf install -y --nogpgcheck "$rpm_url" + + +# === ADD MISSING LIX CACHE ACCESS PUBKEY === + +nix_conf=/etc/nix/nix.conf +lix_cache_url="https://cache.lix.systems/" +lix_cache_key="cache.lix.systems:aBnZUw8zA7H35Cz2RyKFVs3H4PlGTLawyY5KRbvJR8o=" + +ensure_list_value() { + local key="$1" value="$2" escaped_value + escaped_value=$(printf '%s' "$value" | sed 's/[\\&]/\\&/g') + + touch "$nix_conf" + + if grep -Eq "^${key}[[:space:]]*=.*${escaped_value}" "$nix_conf"; then + return + fi + + if grep -Eq "^${key}[[:space:]]*=" "$nix_conf"; then + sed -i "s|^${key}[[:space:]]*= *\\(.*\\)|${key} = \\1 ${escaped_value}|" "$nix_conf" + else + echo "${key} = ${value}" >>"$nix_conf" + fi +} + +ensure_list_value "substituters" "$lix_cache_url" +ensure_list_value "trusted-public-keys" "$lix_cache_key" + +# === SEED STORE FOR FIRST BOOT (copied into /var on boot) === + +if compgen -G "/nix/*" >/dev/null; then + rsync -aH --delete /nix/ /usr/share/nix-store/ + rm -rf /nix/* +fi diff --git a/files/system/etc/selinux/targeted/contexts/files/file_contexts.local b/files/system/etc/selinux/targeted/contexts/files/file_contexts.local new file mode 100644 index 0000000..541fc38 --- /dev/null +++ b/files/system/etc/selinux/targeted/contexts/files/file_contexts.local @@ -0,0 +1,2 @@ +/nix(/.*)? system_u:object_r:bin_t:s0 +/var/lib/nix-store(/.*)? system_u:object_r:bin_t:s0 diff --git a/files/system/etc/systemd/system/nix-daemon.service.d/selinux.conf b/files/system/etc/systemd/system/nix-daemon.service.d/selinux.conf new file mode 100644 index 0000000..1b14e8b --- /dev/null +++ b/files/system/etc/systemd/system/nix-daemon.service.d/selinux.conf @@ -0,0 +1,5 @@ +[Service] +# Run the daemon unconfined to avoid SELinux denials on the Nix store binaries. +SELinuxContext=system_u:system_r:unconfined_service_t:s0 +ExecStart= +ExecStart=/usr/bin/nix-daemon-wrapper.sh --daemon diff --git a/files/system/usr/bin/mount-nix.sh b/files/system/usr/bin/mount-nix.sh new file mode 100755 index 0000000..b580557 --- /dev/null +++ b/files/system/usr/bin/mount-nix.sh @@ -0,0 +1,58 @@ +#!/usr/bin/env bash +set -euo pipefail + +# Bind-mount /var/lib/nix-store to /nix. +# If /var/lib/nix-store is empty, seed it from the baked store in /usr/share/nix-store. + +mkdir -p /usr/share/nix-store /var/lib/nix-store /nix + +copy_seed_store() { + if command -v rsync >/dev/null 2>&1; then + rsync -aH --delete /usr/share/nix-store/ /var/lib/nix-store/ + else + cp -a /usr/share/nix-store/. /var/lib/nix-store/ + fi +} + +sync_missing_store() { + # Ensure any baked store paths exist in /var without clobbering user additions. + if command -v rsync >/dev/null 2>&1; then + rsync -aH --ignore-existing /usr/share/nix-store/store/ /var/lib/nix-store/store/ + fi +} + +ensure_system_profile() { + local seed_profile="/usr/share/nix-store/var/nix/profiles/system" + local target_profile="/var/lib/nix-store/var/nix/profiles/system" + + mkdir -p /var/lib/nix-store/var/nix/profiles + + if { [ ! -e "$target_profile" ] || [ -L "$target_profile" ] && [ ! -e "$(readlink -f "$target_profile")" ]; } \ + && { [ -e "$seed_profile" ] || [ -L "$seed_profile" ]; }; then + cp -a "$seed_profile" "$target_profile" + fi +} + +if ! mountpoint -q /nix; then + if [ -z "$(ls -A /var/lib/nix-store 2>/dev/null)" ] && compgen -G "/usr/share/nix-store/*" >/dev/null; then + copy_seed_store + fi + + ensure_system_profile + sync_missing_store + + mount --bind /var/lib/nix-store /nix + # Force an executable SELinux context on the bind mount so systemd can exec nix-daemon. + # Use a permissive fallback if the label option is rejected. + if ! mount -o remount,bind,exec,context=system_u:object_r:bin_t:s0 /nix 2>/dev/null; then + mount -o remount,bind,exec /nix + fi + + # Ensure daemon paths exist and labels are sane. + if command -v systemd-tmpfiles >/dev/null 2>&1; then + systemd-tmpfiles --create /usr/lib/tmpfiles.d/nix-daemon.conf + fi + if command -v restorecon >/dev/null 2>&1; then + restorecon -RF /var/lib/nix-store /nix || true + fi +fi diff --git a/files/system/usr/bin/nix-daemon-wrapper.sh b/files/system/usr/bin/nix-daemon-wrapper.sh new file mode 100644 index 0000000..bf2cf58 --- /dev/null +++ b/files/system/usr/bin/nix-daemon-wrapper.sh @@ -0,0 +1,2 @@ +#!/usr/bin/env bash +exec /nix/var/nix/profiles/system/bin/nix-daemon "$@" diff --git a/files/system/usr/lib/systemd/system/nix-overlay.service b/files/system/usr/lib/systemd/system/nix-overlay.service new file mode 100644 index 0000000..e0b8c8e --- /dev/null +++ b/files/system/usr/lib/systemd/system/nix-overlay.service @@ -0,0 +1,15 @@ +[Unit] +Description=Bind-mount /var/lib/nix-store to /nix +DefaultDependencies=no +After=local-fs.target +RequiresMountsFor=/var /var/lib/nix-store +Before=nix-daemon.service nix-daemon.socket +ConditionPathExists=/usr/bin/mount-nix.sh + +[Service] +Type=oneshot +ExecStart=/usr/bin/mount-nix.sh +RemainAfterExit=yes + +[Install] +WantedBy=sysinit.target diff --git a/recipes/recipe.yml b/recipes/recipe.yml index 933a29d..334c70e 100644 --- a/recipes/recipe.yml +++ b/recipes/recipe.yml @@ -1,9 +1,9 @@ --- # yaml-language-server: $schema=https://schema.blue-build.org/recipe-v1.json # image will be published to ghcr.io// -name: randofin-os +name: deinonyxus # description will be included in the image's metadata -description: This is my personal spin based on the latest bluefin image. +description: Bluefin DX with Nix and sprinkles. # the base image to build on top of (FROM) and the version tag to use base-image: ghcr.io/ublue-os/bluefin-dx @@ -12,15 +12,45 @@ image-version: latest # latest is also supported if you want new updates ASAP # module configuration, executed in order # you can include multiple instances of the same module modules: + + - type: os-release + properties: + ID: deinonyxus + NAME: Deinonyxus + PRETTY_NAME: Deinonyxus (Bluefin DX) + DEFAULT_HOSTNAME: deinonyxus + HOME_URL: https://codeberg.org/randogoth/deinonyxus + SUPPORT_URL: https://codeberg.org/randogoth/deinonyxus/issues + BUG_REPORT_URL: https://codeberg.org/randogoth/deinonyxus/issues + - type: files files: - source: system destination: / # copies files/system/* (* means everything inside it) into your image's root folder / + - type: script + scripts: + - install-lix.sh + + - type: systemd + system: + enabled: + - nix-overlay.service + - nix-daemon.service + - nix-daemon.socket + - type: dnf + repos: + files: + add: + - https://repo.vscodium.dev/vscodium.repo install: packages: + - repo: vscodium + packages: + - codium - syncthing + - uv - waydroid remove: packages: @@ -39,8 +69,10 @@ modules: - docker-ce-rootless-extras - docker-compose-plugin - docker-model-plugin - - containerd - - moby-engine + + - type: justfiles + include: + - nixpkgs.just - type: default-flatpaks configurations: @@ -49,7 +81,6 @@ modules: # If no repo information is specified, Flathub will be used by default install: # system flatpaks we want all users to have and not remove - net.waterfox.waterfox - - com.vscodium.codium - org.telegram.desktop remove: # replace default Firefox with Waterfox - org.mozilla.firefox