Skip to content

Conversation

@Anshgrover23
Copy link

@Anshgrover23 Anshgrover23 commented Jan 10, 2026

Screen.Recording.2026-01-14.at.5.33.35.PM.mov

Summary by CodeRabbit

  • New Features

    • Complete Cortex visual branding (boot splash, GRUB/GDM themes, wallpapers, terminal profile, MOTD, neofetch) and an installable branding package.
    • First‑boot provisioning service to configure system state on initial boot.
    • Multiple unattended installer partitioning recipes (Simple, LVM, RAID1, FDE).
  • Documentation

    • Developer Guide, Quick Start, automated installation guide, and detailed theme/asset specs.
  • Chores

    • Modularized build tooling, new dependency installer, and CI updated for multi‑arch ISO builds, validation, and packaging.

✏️ Tip: You can customize this high-level summary in your review settings.

@coderabbitai
Copy link

coderabbitai bot commented Jan 10, 2026

Note

Other AI code review bot(s) detected

CodeRabbit has detected other AI code review bot(s) in this pull request and will avoid duplicating their findings in the review comments. This may lead to a less comprehensive review.

📝 Walkthrough

Walkthrough

Adds comprehensive Cortex branding (assets, themes, packaging), reorganizes build tooling into script-driven commands and CI multi-arch jobs, introduces preseeds and live-build hooks, and provides an idempotent, network-aware first-boot provisioning service/script alongside developer documentation.

Changes

Cohort / File(s) Summary
Version Control
\.gitignore``
New .gitignore to exclude build artifacts, branding outputs, temporary files, IDE metadata, and logs.
Build System Core
Makefile, \scripts/build.sh`, `scripts/install-deps.sh``
Makefile refactored to public targets delegating to external BUILD_SCRIPT; scripts/build.sh rewritten into a subcommand dispatcher (build/validate/test/package/clean/sync/etc.); scripts/install-deps.sh adds a dependency installer for build environments.
CI / Release
\.github/workflows/build-iso.yml, \repository/scripts/repo-manage.sh``
CI reworked into validate → branding-package → matrixed Dockerized ISO builds + ISO tests and SBOM handling; repo-manage.sh ensures a Packages file exists instead of failing silently.
Branding Guidelines & Asset Generation
\branding/BRAND-GUIDELINES.md`, `branding/generate-assets.sh`, `branding//ASSETS.md`, `branding//*.{svg,md,sh}``
Adds brand guidelines, ImageMagick generator script, and asset documentation. Verify ImageMagick dependency, output directories, and permissions.
Themes & Display Managers
\branding/gdm/.css`, `branding/gdm/gdm-branding.conf`, `branding/grub/cortex/theme.txt`, `branding/plymouth/cortex/`, `iso/live-build/config/includes.chroot/usr/share/plymouth/themes/cortex/*``
New GDM CSS/conf, GRUB theme, and Plymouth theme/script with progress/update/password callbacks. Confirm asset paths, theme registration, and Plymouth callback bindings.
MOTD, Neofetch & OS Identity
\branding/motd/`, `branding/neofetch/`, `branding/os-release/*``
Adds MOTD scripts (banner/sysinfo/updates/footer), neofetch config and ASCII banner, and OS release/issue files. Ensure executable bits and update-motd ordering.
Live-build Hooks & Image Includes
\iso/live-build/config/hooks/live/`, `iso/live-build/config/includes.chroot/`, `iso/live-build/config/bootloaders/grub-pc/`, `iso/live-build/config/package-lists/``
New live-build hooks apply branding, GNOME dconf, PAM, wallpapers, Plymouth/GRUB adjustments, and enable first-boot unit; new GRUB configs and package lists for live images. Review idempotency and conditional checks.
Preseed & Partition Recipes
\iso/preseed/partitioning/*.preseed``
New preseeds for simple BIOS/UEFI, LVM UEFI, FDE UEFI, and RAID1 UEFI. Inspect embedded passphrase placeholders and unattended defaults for security implications.
Provisioning & First-boot
\iso/provisioning/cortex-first-boot.service`, `iso/provisioning/first-boot.sh`, `iso/live-build/config/hooks/live/0100-cortex-config.hook.chroot``
Adds hardened systemd one-shot unit and an idempotent, network-aware first-boot provisioning script; hook enables renamed unit and updates unit paths. Validate network-dependent steps, state marker path, and unit hardening flags.
Debian Packaging & Installer Hooks
\packages/cortex-branding/DEBIAN/`, `packages/-meta/DEBIAN/control``
New cortex-branding package (control, postinst, prerm) and meta-package controls (core/full/secops). Review package dependencies and postinst/prerm side effects (plymouth, grub, dconf, MOTD).
Documentation
\README.md`, `docs/DEVELOPERS.md`, `docs/QUICK-START.md`, `docs/automated-installation.md``
New and expanded docs covering developer workflows, quick start, and unattended installation guides; README updated for new tooling/artifact names.
Removed / Refactored
\iso/live-build/auto/*`, `iso/live-build/config/includes.chroot/usr/lib/cortex/firstboot.sh`, `iso/live-build/config/package-lists/cortex-core.list.chroot` (contents removed)`
Deleted legacy auto-build scripts and in-tree firstboot script; core package list content removed or migrated to new package lists/meta-packages/hooks.

Sequence Diagram(s)

sequenceDiagram
  participant Systemd as systemd
  participant Unit as cortex-first-boot.service
  participant Script as /opt/cortex/provisioning/first-boot.sh
  participant Network as Network
  participant APT as APT
  participant SSHD as sshd
  participant System as System

  Systemd ->> Unit: start (ConditionPathExists)
  Unit ->> Script: ExecStart
  Script ->> System: check .first-boot-complete
  alt not completed
    Script ->> Network: probe connectivity
    Network -->> Script: reachable / unreachable
    alt reachable
      Script ->> APT: add repo, apt update/upgrade
      APT -->> Script: success/fail
    end
    Script ->> SSHD: generate/verify host keys, apply SSH hardening
    Script ->> System: configure users, sudo, firewall, fail2ban, unattended-upgrades
    Script ->> System: cleanup installer artifacts
    Script ->> System: write .first-boot-complete
    Script ->> Systemd: disable/stop first-boot unit
  else already completed
    Script -->> Unit: exit
  end
  Script -->> Systemd: exit status
Loading

Estimated code review effort

🎯 5 (Critical) | ⏱️ ~120 minutes

Poem

🐰 I hop and stamp the branding seed,

I paint the boot, the login, theme and feed.
GRUB and Plymouth pulse in neon light,
First‑boot hums, provisions through the night,
ISO tucked warm — hop, sleep tight.

🚥 Pre-merge checks | ✅ 2 | ❌ 1
❌ Failed checks (1 warning)
Check name Status Explanation Resolution
Docstring Coverage ⚠️ Warning Docstring coverage is 54.41% which is insufficient. The required threshold is 80.00%. Write docstrings for the functions missing them to satisfy the coverage threshold.
✅ Passed checks (2 passed)
Check name Status Explanation
Title check ✅ Passed The title accurately reflects the primary objective of this changeset: establishing a complete Cortex Linux distribution ISO with dual-mode boot functionality (live environment and full installer).
Description Check ✅ Passed Check skipped - CodeRabbit’s high-level summary is enabled.

✏️ Tip: You can configure your own custom pre-merge checks in the settings.

✨ Finishing touches
  • 📝 Generate docstrings

Comment @coderabbitai help to get the list of available commands and usage tips.

@gemini-code-assist
Copy link

Summary of Changes

Hello @Anshgrover23, I'm Gemini Code Assist1! I'm currently reviewing this pull request and will post my feedback shortly. In the meantime, here's a summary to help you and other reviewers quickly get up to speed!

This pull request introduces a foundational build system for Cortex Linux, enabling the creation of diverse ISO images tailored for various use cases, from minimal servers to security-hardened desktops. It standardizes the distribution's visual identity through comprehensive branding and streamlines the post-installation experience with automated provisioning, significantly enhancing the development and deployment workflow.

Highlights

  • Refactored ISO Build System: The Makefile has been completely overhauled to support building Cortex Linux ISOs for multiple profiles (core, full, secops) and architectures (amd64, arm64), replacing the previous simplified build targets with a more modular and robust system.
  • Comprehensive Branding Integration: Extensive branding assets and configurations have been added across the distribution, including custom themes for Plymouth (boot splash), GRUB (bootloader), GDM (login screen), desktop wallpapers, Message of the Day (MOTD), and Neofetch, ensuring a consistent visual identity.
  • Automated Installation Profiles: New preseed files introduce automated installation for distinct profiles: 'core' for minimal server setups, 'full' for desktop and development environments, and 'secops' for security-hardened systems with Full Disk Encryption (FDE) and advanced security tools.
  • First-Boot Provisioning Script: A new first-boot.sh script and systemd service automate post-installation setup, handling tasks like machine-id generation, sudo configuration, SSH hardening, firewall setup, and profile-specific configurations (e.g., Docker for 'full', AppArmor for 'secops').
  • Detailed Documentation: New documentation files (DEVELOPERS.md, QUICK-START.md, automated-installation.md) have been added to guide developers on building ISOs and contributing, and users on quick setup and automated installations.
  • Debian Package Definitions: Debian package control files have been introduced for cortex-branding, cortex-core, cortex-full, and cortex-secops, enabling modular installation and management of Cortex Linux components.
Using Gemini Code Assist

The full guide for Gemini Code Assist can be found on our documentation page, here are some quick tips.

Invoking Gemini

You can request assistance from Gemini at any point by creating a comment using either /gemini <command> or @gemini-code-assist <command>. Below is a summary of the supported commands on the current page.

Feature Command Description
Code Review /gemini review Performs a code review for the current pull request in its current state.
Pull Request Summary /gemini summary Provides a summary of the current pull request in its current state.
Comment @gemini-code-assist Responds in comments when explicitly tagged, both in pull request comments and review comments.
Help /gemini help Displays a list of available commands.

Customization

To customize Gemini Code Assist for GitHub experience, repository maintainers can create a configuration file and/or provide a custom code review style guide (such as PEP-8 for Python) by creating and adding files to a .gemini/ folder in the base of the repository. Detailed instructions can be found here.

Limitations & Feedback

Gemini Code Assist may make mistakes. Please leave feedback on any instances where its feedback is incorrect or counter productive. You can react with 👍 and 👎 on @gemini-code-assist comments. If you're interested in giving your feedback about your experience with Gemini Code Assist for Github and other Google products, sign up here.

You can also get AI-powered code generation, chat, as well as code reviews directly in the IDE at no cost with the Gemini Code Assist IDE Extension.

Footnotes

  1. Review the Privacy Notices, Generative AI Prohibited Use Policy, Terms of Service, and learn how to configure Gemini Code Assist in GitHub here. Gemini can make mistakes, so double check it and use code with caution.

Copy link

@gemini-code-assist gemini-code-assist bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Code Review

This is an impressive and comprehensive pull request that bootstraps an entire Linux distribution build system. The structure is well-thought-out, with a powerful Makefile, support for different profiles, and a clear separation of concerns for branding, configuration, and documentation. My review focuses on improving the robustness and security of the build and provisioning process. I've identified a few areas where error handling can be improved to make the build process more reliable, and a critical security issue related to the handling of the default passphrase for full-disk encryption that needs to be addressed.

Copy link

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 12

Note

Due to the large number of review comments, Critical severity comments were prioritized as inline comments.

🤖 Fix all issues with AI agents
In @branding/gdm/gdm-branding.conf:
- Around line 1-8: The GDM config references a non-existent logo path
'/usr/share/cortex/logos/cortex-logo-light.svg'; update the login-screen logo
setting to the actual filename 'cortex-logo.svg' and ensure the asset is
installed to that path by either modifying the install-branding.sh script to
copy 'branding/gdm/cortex-logo.svg' into '/usr/share/cortex/logos/' (creating
the directory if needed) or change the config to point to wherever
install-branding.sh currently places the logo; update the value in
[org/gnome/login-screen] (logo=) and adjust install-branding.sh accordingly so
the file and path match.
- Around line 20-28: The GDM config sets icon-theme='cortex-icons' in the
[org/gnome/desktop/interface] section but that theme isn’t provided; either
remove that cortex-icons entry or install/ship the cortex-icons theme and ensure
it’s referenced identically in the live session hooks (the
live/99-configure-gnome.hook.chroot usage that currently uses
icon-theme='Adwaita'); update the GDM key icon-theme='cortex-icons' to
icon-theme='Adwaita' if you prefer to align with the live session, or add the
cortex-icons theme package and update both GDM and live session hooks to use it.

In @docs/DEVELOPERS.md:
- Around line 9-15: The documented live-build version requirement is invalid;
update the version string in the DEVELOPER doc block where the live-build
install instructions and the "Verify live-build version" check appear so it
references a real release for your supported distro (e.g., change "live-build >=
1:20210814" to a valid version such as "live-build >= 1:20230502" for bookworm+
or to the specific minimum available in your minimum-supported release); ensure
the comment and the dpkg verification line reflect the chosen valid version.

In @docs/QUICK-START.md:
- Around line 54-63: The example commands in the docs use incorrect flags;
replace `cortex-upgrade --check` with the supported `cortex-upgrade plan` (and
mention the valid subcommands: plan, execute, rollback, status) and change
`cortex-verify --quick` to just `cortex-verify` (or note supported output flags
like `--json`) while leaving `cortex-gpu status` as-is; update the README
examples and any occurrences of `cortex-upgrade --check` and `cortex-verify
--quick` to the correct command forms (`cortex-upgrade plan` and
`cortex-verify`) and optionally add a brief note about `--json` output for
`cortex-verify`.

In @iso/live-build/config/includes.binary/boot/grub/themes/cortex/theme.txt:
- Line 12: Remove the stray '+' prefix from GRUB theme block declarations (e.g.,
boot_menu) so block names are bare identifiers followed by their opening brace;
locate each block declaration that begins with '+' and delete the '+' character
so the syntax matches "block_name { ... }" for all theme blocks.

In @iso/live-build/config/includes.chroot/boot/grub/themes/cortex/theme.txt:
- Around line 11-32: The theme file contains literal '+' characters before block
names which breaks GRUB parsing; remove the leading '+' characters from the
block definitions such as boot_menu, every label block, and progress_bar so the
blocks read e.g. "boot_menu {", "label {", and "progress_bar {" instead of "+
boot_menu {" etc.; update any occurrences (e.g., boot_menu, label, progress_bar)
throughout theme.txt so all block headers start without '+' and ensure
spacing/indentation remains valid for the GRUB theme parser.

In
@iso/live-build/config/includes.chroot/usr/share/plymouth/themes/cortex/cortex.script:
- Around line 98-137: display_normal_callback may call password_dialogue_opacity
before globals set by password_dialogue_setup, causing null-derefs; modify
password_dialogue_opacity to guard uses of global.password_entry,
global.password_prompt, and iterate only if global.password_bullets is non-null
(or initialize those globals to sensible defaults at top-level), e.g., check
each global is defined before calling .sprite or looping, and return early if
none exist so display_normal_callback can safely call it.

In @iso/preseed/profiles/cortex-full.preseed:
- Around line 23-30: The preseed currently sets a deterministic/placeholder
password via the debconf key d-i passwd/user-password-crypted (and related keys
like d-i passwd/username and d-i user-setup/allow-password-weak), which creates
a predictable backdoor; remove the hardcoded d-i passwd/user-password-crypted
and user-setup/allow-password-weak lines so the installer prompts for a password
interactively, or convert the value into a required build-time substitution
variable (fail the build if unset) and document the substitution; ensure any
CI/build script that generates this file validates the substitution and rejects
commits that still contain the placeholder.

In @iso/preseed/profiles/cortex-secops.preseed:
- Around line 47-51: The preseed embeds a static full-disk-encryption passphrase
via the partman-crypto/passphrase and partman-crypto/passphrase-again entries,
which exposes all builds; remove these hardcoded values and instead require
interactive entry or generate a unique passphrase per ISO build and display it
once during build output; alternatively disable automated FDE in the preseed
(remove partman-crypto/* passphrase keys and related auto-enable flags) and
document manual FDE setup, and if you implement per-build generation use the
REQUIRE_FDE_PASSPHRASE_CHANGE variable only for enforcement and ensure build
tooling injects the secret securely (not committed in the preseed).

In @packages/cortex-branding/DEBIAN/postinst:
- Around line 10-25: Postinst sets GRUB_THEME in /etc/default/grub but prerm
does not restore it, leaving a broken GRUB_THEME pointing to a removed file;
modify postinst to save the previous GRUB_THEME value (or empty marker) to
/var/lib/cortex-branding/grub-theme.prev before writing the new GRUB_THEME and
modify prerm to read /var/lib/cortex-branding/grub-theme.prev and either restore
the original GRUB_THEME line in /etc/default/grub (if a previous value exists)
or remove the GRUB_THEME line entirely (if marker indicates none), then run
update-grub (or update-grub 2>/dev/null || true) to apply changes; reference the
scripts postinst and prerm, the GRUB_THEME entry in /etc/default/grub, and the
state file /var/lib/cortex-branding/grub-theme.prev when implementing.

In @packages/cortex-branding/DEBIAN/prerm:
- Around line 6-21: The prerm currently calls "plymouth-set-default-theme -R"
which only rebuilds initrd for the current theme; change the flow so the package
either saves the prior theme in postinst (e.g., write the original theme name to
a persistent state file) and then prerm reads that state and restores it, or
replace "-R" with "-r -R" to reset to system default and rebuild; likewise, stop
indiscriminately chmod'ing all /etc/update-motd.d/??-* scripts — have postinst
record exactly which MOTD scripts this package disabled (e.g., write their
filenames to a state file) and make prerm iterate only over that recorded list
to re-enable (chmod +x) those files, leaving other scripts untouched (refer to
the postinst logic that disables non-cortex scripts in lines 34–39 for exact
criteria).

In @packages/cortex-core/DEBIAN/control:
- Around line 1-20: The control file's Depends field lists non-existent packages
causing install failures; open the DEBIAN control and update the Depends line to
reference only existing packages (keep cortex-archive-keyring), remove or
replace cortex-upgrade and cortex-verify with their correct package names if
they exist, and replace cortex-gpu with the actual GPU package(s) (e.g.,
cortex-gpu-amd and/or cortex-gpu-nvidia) or change it to a Suggests/Provides if
it’s meant to be virtual; ensure the Depends field remains a comma-separated
list and adjust Recommends/Suggests accordingly if you move packages out of
Depends.
🟠 Major comments (18)
branding/motd/99-cortex-footer-14-24 (1)

14-24: Verify that MOTD tips reference only universally available commands.

The MOTD is installed system-wide for all installations, but several referenced commands are either missing or conditionally available:

  • cortex — no implementation found in codebase
  • cortex-update — not found (only 20-cortex-updates script exists)
  • lynis — only included in the cortex-secops profile, not in base installations

Consider either implementing these commands, making the MOTD profile-aware, or replacing these tips with universally available alternatives.

branding/motd/10-cortex-sysinfo-17-67 (1)

17-67: Fix fallback correctness for DISTRO/IP_ADDR/USERS and guard numeric comparisons.

Right now, several || echo ... fallbacks won’t trigger because the last command in the pipeline (cut/awk/wc) commonly exits 0 even when the “real” source command failed or produced empty output.

Proposed diff
--- a/branding/motd/10-cortex-sysinfo
+++ b/branding/motd/10-cortex-sysinfo
@@
 HOSTNAME=$(hostname)
-DISTRO=$(grep PRETTY_NAME /etc/os-release 2>/dev/null | cut -d'"' -f2 || echo "Cortex Linux")
+DISTRO=$(
+  awk -F= '/^PRETTY_NAME=/{gsub(/^"|"$/, "", $2); print $2; exit}' /etc/os-release 2>/dev/null
+)
+[ -n "$DISTRO" ] || DISTRO="Cortex Linux"
@@
-IP_ADDR=$(hostname -I 2>/dev/null | awk '{print $1}' || echo "N/A")
+IP_ADDR="$(hostname -I 2>/dev/null | awk '{print $1}')"
+[ -n "$IP_ADDR" ] || IP_ADDR="N/A"
@@
-USERS=$(who 2>/dev/null | wc -l || echo "0")
+USERS="$(who 2>/dev/null | wc -l 2>/dev/null)"
+case "$USERS" in (''|*[!0-9]*) USERS=0 ;; esac
@@
 mem_color() {
-    if [ "$1" -lt 50 ]; then echo -e "${GREEN}";
-    elif [ "$1" -lt 80 ]; then echo -e "${YELLOW}";
+    case "$1" in (''|*[!0-9]*) set -- 0 ;; esac
+    if [ "$1" -lt 50 ]; then echo -e "${GREEN}";
+    elif [ "$1" -lt 80 ]; then echo -e "${YELLOW}";
     else echo -e "${RED}"; fi
 }
@@
 disk_color() {
-    if [ "$1" -lt 70 ]; then echo -e "${GREEN}";
-    elif [ "$1" -lt 90 ]; then echo -e "${YELLOW}";
+    case "$1" in (''|*[!0-9]*) set -- 0 ;; esac
+    if [ "$1" -lt 70 ]; then echo -e "${GREEN}";
+    elif [ "$1" -lt 90 ]; then echo -e "${YELLOW}";
     else echo -e "${RED}"; fi
 }
iso/preseed/partitioning/simple-uefi.preseed-10-12 (1)

10-12: Installer robustness: avoid hardcoding /dev/sda, add explicit ESP mountpoint, and clarify the EFI debconf key.

  • /dev/sda will break on NVMe, virtio, and other storage types. Use partman/early_command to set partman-auto/disk dynamically (e.g., list-devices disk | head -n1), or reference stable /dev/disk/by-id or /dev/disk/by-path instead.
  • Add explicit mountpoint{ /boot/efi } to the ESP recipe (lines 20–26) for clarity and to avoid relying on undocumented partman defaults.
  • The name partman-efi/non_efi_system is counterintuitive (setting it true means "force UEFI-only"), but your usage is correct; document it locally for maintainers.

Same issues apply to all UEFI preseed variants: fde-uefi, lvm-uefi, raid1-uefi, and simple-uefi.

iso/preseed/partitioning/fde-uefi.preseed-33-34 (1)

33-34: Security concern: Hardcoded LUKS passphrase in version-controlled file.

The plaintext passphrase cortex-temp-passphrase is committed to the repository. While comments indicate it should be changed at first boot, this creates risk:

  • Users may forget or skip the passphrase change step
  • The default passphrase becomes publicly known if the repo is public
  • Automated scanners may flag this as a credential leak

Consider using a preseed hook to prompt for passphrase entry, or document this very prominently in installation guides.

iso/preseed/partitioning/raid1-uefi.preseed-62-67 (1)

62-67: Missing LVM logical volume definitions inside RAID.

The preseed defines the RAID arrays and volume group cortex-vg, but doesn't include the logical volume definitions for root (20GB), swap (8GB), and home (remaining) as described in the header comments (lines 10-13). Without these, the installation will fail or produce unexpected results.

Compare with lvm-uefi.preseed lines 44-61 which properly define the LV layout within the expert_recipe.

packages/cortex-branding/DEBIAN/postinst-26-40 (1)

26-40: Track which MOTD scripts you disable (avoid blanket chmod behavior on uninstall).
The loop disables “non-cortex” scripts (Lines 34-39) but you don’t record which ones changed—so prerm can’t safely revert without re-enabling everything.

Proposed change (write disabled list for prerm)
         if [ -d /etc/update-motd.d ]; then
@@
             # Disable default Ubuntu/Debian MOTD scripts
+            mkdir -p /var/lib/cortex-branding
+            : > /var/lib/cortex-branding/motd-disabled.list
             for f in /etc/update-motd.d/??-* ; do
                 case "$(basename $f)" in
                     *cortex*) ;;  # Keep our scripts
-                    *) chmod -x "$f" 2>/dev/null || true ;;
+                    *)
+                        chmod -x "$f" 2>/dev/null || true
+                        printf '%s\n' "$f" >> /var/lib/cortex-branding/motd-disabled.list
+                        ;;
                 esac
             done
         fi
iso/live-build/config/bootloaders/grub-pc/theme.cfg-5-8 (1)

5-8: Fix GRUB theme path — it will not load as configured.

set theme=/boot/grub/live-theme/theme.txt (Line 6) does not match the actual deployment path used throughout the codebase. All build hooks, installation scripts, and the Makefile consistently deploy the theme to /boot/grub/themes/cortex/theme.txt. GRUB will fail to find the file at the configured path and silently fall back to defaults, removing custom branding.

Proposed fix
-# Always load Cortex theme
-set theme=/boot/grub/live-theme/theme.txt
+# Always load Cortex theme
+set theme=/boot/grub/themes/cortex/theme.txt
 set menu_color_normal=light-gray/black
 set menu_color_highlight=cyan/black
branding/wallpapers/ASSETS.md-1-19 (1)

1-19: Wallpaper assets incomplete: only single resolution per wallpaper exists instead of documented 3 resolutions.

ASSETS.md requires all 6 wallpapers at three resolutions (4K, QHD, FHD), but only 6 PNG files exist in branding/wallpapers/images/ — one per wallpaper. Either the documentation should be updated to reflect single-resolution assets, or the missing QHD and FHD variants (12 additional files) must be generated to match the documented requirements.

branding/gdm/INSTALL.md-49-71 (1)

49-71: Method 3 + Troubleshooting: fix glib-compile-resources invocation and restore commands (current doc is likely broken).

  • glib-compile-resources typically needs --target (and often --sourcedir) to produce the expected output file.
  • The XML references gnome-shell.css, but the guide never creates/copies gnome-shell.css from cortex-gdm.css.
  • The “restore backup” command uses relative filenames and won’t restore the actual backed-up paths; also consider restoring the .gresource backup (not just CSS).
Proposed doc fix
 # Build resource
-glib-compile-resources gnome-shell-theme.gresource.xml
+cp cortex-gdm.css gnome-shell.css
+glib-compile-resources gnome-shell-theme.gresource.xml \
+  --target=gnome-shell-theme.gresource \
+  --sourcedir=.

 # Install
 sudo cp gnome-shell-theme.gresource /usr/share/gnome-shell/
 ### Black screen after changes
 1. Boot to recovery mode
-2. Restore backup: `sudo cp gnome-shell.css.backup gnome-shell.css`
+2. Restore backup:
+   - `sudo cp /usr/share/gnome-shell/theme/gnome-shell.css.backup /usr/share/gnome-shell/theme/gnome-shell.css`
+   - `sudo cp /usr/share/gnome-shell/gnome-shell-theme.gresource.backup /usr/share/gnome-shell/gnome-shell-theme.gresource`
 3. Reboot normally

Also applies to: 96-99

iso/live-build/config/bootloaders/grub-pc/install_start_text.cfg-1-4 (1)

1-4: Fix duplicate --hotkey=i entries causing menu collision. Multiple menuentry definitions across install_start_text.cfg, install_text.cfg, install_start_gui.cfg, and install_gui.cfg use --hotkey=i. When GRUB loads these configurations, only the first match becomes active, making the hotkey unreliable and breaking expected menu behavior. Each installer variant (text/GUI, Start vs. Install, themed vs. unthemed) should have unique hotkeys.

iso/preseed/profiles/cortex-full.preseed-36-83 (1)

36-83: Document EFI-only limitation and provide BIOS alternative for profiles.

These three profiles (cortex-core, cortex-full, cortex-secops) all hardcode /dev/sda and use GPT + EFI-only partitioning recipes. This will fail on BIOS-only systems. While simple-bios.preseed exists in iso/preseed/partitioning/, it is not integrated or documented as an alternative.

At minimum, add a comment in each profile documenting:

  1. That /dev/sda is unconditionally targeted (state how to override via boot parameter if supported)
  2. That these are EFI-only; provide guidance to use the BIOS preseed for legacy systems
  3. Or unify the recipe conditionally (like the main cortex.preseed does with $iflabel{ gpt }) to support both architectures
packages/cortex-secops/DEBIAN/control-6-38 (1)

6-38: Remove or replace ossec-hids from Recommends — it is not available in Debian 12 (bookworm) official repositories.

The package ossec-hids requires a third-party repository (Atomicorp/Wazuh) and will cause apt resolver errors if that repo is not configured. The other flagged packages (tiger, tripwire, psad) do exist in bookworm. Either remove ossec-hids, document the third-party repo requirement explicitly, or substitute an alternative that exists in official repos (e.g., wazuh-agent if using Wazuh's official repo, or remove it entirely if it is optional).

iso/preseed/partitioning/simple-bios.preseed-11-11 (1)

11-11: Hard-coded disk path limits installation flexibility.

The fixed /dev/sda path will fail on systems with NVMe drives (/dev/nvme0n1), virtio (/dev/vda), or when the target disk is not the first SCSI device. This significantly restricts where this preseed can be used.

💡 Suggested fix: Use dynamic disk detection

Consider one of these approaches:

Option 1: Omit the disk directive to let the installer prompt or auto-detect:

 d-i partman-auto/method string regular
-d-i partman-auto/disk string /dev/sda

Option 2: Use a preseed early_command to detect the first available disk dynamically:

+# Detect first available disk dynamically
+d-i partman/early_command string \
+    DISK=$(list-devices disk | head -n1); \
+    debconf-set partman-auto/disk "$DISK"
 d-i partman-auto/method string regular
-d-i partman-auto/disk string /dev/sda
iso/preseed/cortex-base.preseed-36-44 (1)

36-44: Clarify weak password handling and first-boot provisioning dependency.

The preseed configuration creates a default cortex user with a placeholder password hash ($6$rounds=4096$cortexsalt$placeholder) and relies on first-boot provisioning or cloud-init to set a real password. This is operationally risky if:

  1. First-boot provisioning fails or is skipped.
  2. The system is used before first-boot provisioning runs.
  3. An operator mistakes the placeholder for a real password.

Recommendations:

  • Document clearly in comments that this password must be changed before the system is accessible to users.
  • Consider enforcing password expiration in PAM (e.g., chage -d 0 cortex) to force a password change on first login if first-boot provisioning is unavailable.
  • Add validation/logging in first-boot provisioning to confirm password was changed.
  • Consider requiring a secure boot sequence that ensures first-boot provisioning runs before network access is granted.
branding/os-release/os-release-1-13 (1)

1-13: Fix incorrect GitHub repository name in URLs.

The SUPPORT_URL and BUG_REPORT_URL reference a non-existent repository cortexlinux/cortex-distro. The actual repository is cortexlinux/cortex. Update lines 9–10:

SUPPORT_URL="https://github.com/cortexlinux/cortex/discussions"
BUG_REPORT_URL="https://github.com/cortexlinux/cortex/issues"
docs/automated-installation.md-324-328 (1)

324-328: Security: Reconsider exposing the default FDE passphrase in documentation.

Documenting the default FDE passphrase (cortex-temp-fde-passphrase) in plain text creates a security risk. If users deploy SecOps installations without changing this passphrase, attackers with access to this documentation can decrypt the disk.

Consider:

  1. Using a randomly generated passphrase during installation that's displayed once
  2. Forcing passphrase change on first boot for SecOps profile
  3. At minimum, adding a more prominent warning about this being publicly documented
branding/generate-assets.sh-9-11 (1)

9-11: Add ImageMagick version compatibility check.

The script uses the magick command (ImageMagick 7 syntax) extensively, but Debian Bookworm ships ImageMagick 6 by default, which uses convert instead. Without a compatibility layer, the script will fail on systems with only IM6 installed.

Implement the suggested wrapper to detect and use the available command:

Compatibility wrapper
 set -e

 SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)"
+
+# ImageMagick compatibility: IM7 uses 'magick', IM6 uses 'convert'
+if command -v magick &>/dev/null; then
+    IM_CMD="magick"
+elif command -v convert &>/dev/null; then
+    IM_CMD="convert"
+else
+    echo "ERROR: ImageMagick not installed"
+    exit 1
+fi

Then replace all magick invocations with $IM_CMD.

iso/live-build/config/includes.chroot/usr/share/plymouth/themes/cortex/cortex.script-138-147 (1)

138-147: Reuse the message sprite instead of allocating a new one per message.
message_callback() creates a new Sprite every time; repeated status updates can stack sprites and waste memory.

Proposed fix
 fun message_callback(text) {
     message.image = Image.Text(text, 0.6, 0.6, 0.7, 1, "Sans 11");
-    message.sprite = Sprite(message.image);
+    if (!message.sprite) {
+        message.sprite = Sprite(message.image);
+    } else {
+        message.sprite.SetImage(message.image);
+    }
     message.x = screen_x + screen_width / 2 - message.image.GetWidth() / 2;
     message.y = screen_y + screen_height - 50;
     message.sprite.SetPosition(message.x, message.y, 10);
 }
🟡 Minor comments (17)
iso/provisioning/first-boot.sh-358-378 (1)

358-378: Inconsistent volume group name in FDE notice.

Line 378 references cortex-sec-vg but the FDE preseed uses cortex-vg as the volume group name. This will confuse users trying to verify their encryption status.

🐛 Proposed fix
 To verify encryption status:
-    sudo cryptsetup status cortex-sec-vg
+    sudo cryptsetup status cortex-vg
branding/plymouth/cortex/ASSETS.md-1-51 (1)

1-51: **Fix Markdown fencing (currently likely to render broken due to mixed and ````).** The file starts/ends with ```` which conflicts with the innerbash block.

Proposed change (use standard triple-backtick fences only)
-````
 # Plymouth Theme Assets
@@
 ```bash
 # Generate placeholder assets (requires ImageMagick)
 convert -size 200x200 xc:transparent \
@@
     progress-bar.png

-````

</details>

</blockquote></details>
<details>
<summary>packages/cortex-branding/DEBIAN/postinst-42-46 (1)</summary><blockquote>

`42-46`: **Guard `dconf update` with a command check, not just file check.**

On minimal or server installations where GDM (`gdm3`) is not installed, the `dconf` command will be absent. While your file check on line 43 ensures the config exists, `dconf` itself may not be present, causing the command to fail (though suppressed by `|| true`).

This pattern is already used elsewhere in the script (line 11 with `command -v`). Apply the same guard:

<details>
<summary>Proposed change</summary>

```diff
         # Update GDM branding
         if [ -f /etc/gdm3/greeter.dconf-defaults ]; then
-            dconf update 2>/dev/null || true
+            command -v dconf >/dev/null 2>&1 && dconf update 2>/dev/null || true
         fi
iso/live-build/config/includes.chroot/boot/grub/themes/cortex/ASSETS.md-42-52 (1)

42-52: Update optional icons section to match actual files present.

The required theme assets and theme.txt references are correct and all PNG files listed in the asset table exist. However, the optional icons section documents six icons (cortex.png, debian.png, linux.png, windows.png, recovery.png, settings.png) but only three are present in the repository: cortex.png, linux.png, and recovery.png. Either add the missing icon files (debian.png, windows.png, settings.png) or update the documentation to reflect only the icons that exist.

The installation instructions assume manual post-installation setup, which is standard; note that in this live-build context, the theme is pre-included in the ISO build process.

branding/gdm/INSTALL.md-7-13 (1)

7-13: Clarify exact install destinations (paths) for “GDM theme / GDM resources”.
Right now the table mixes conceptual destinations (“GDM theme”, “GDM resources”) with one concrete path; it’ll be hard to automate/review correctness across Debian/GNOME versions. Consider listing the exact on-disk paths (and whether assets must be embedded into gnome-shell-theme.gresource).

branding/gdm/cortex-gdm.css-148-151 (1)

148-151: Consider ignoring Biome's unknown pseudo-class warning on .user-list-item:selected.
While :selected is valid in GNOME Shell CSS (part of GTK widget states), Biome's CSS linter will flag it since it's not standard web CSS. The suggestion to add an inline ignore comment is optional for developers who lint locally, but this is not a CI blocker since Biome is not currently invoked in your build pipeline.

-.user-list-item:selected {
+/* biome-ignore lint/correctness/noUnknownPseudoClass: GNOME Shell theme pseudo-class */
+.user-list-item:selected {
     background-color: rgba(107, 33, 168, 0.5);
     border: 1px solid #6B21A8;
 }
packages/cortex-full/DEBIAN/control-12-26 (1)

12-26: firefox-esr package name may not be available on Ubuntu.

Line 22 recommends firefox-esr, which is the Debian package name. Ubuntu typically provides Firefox via snap or as firefox (not firefox-esr). This won't block installation (Recommends vs Depends) but may result in missing Firefox on Ubuntu systems.

Consider a more portable browser recommendation

If targeting both Debian and Ubuntu:

- firefox-esr,
+ firefox-esr | firefox,

This allows either package name to satisfy the recommendation. Alternatively, document this difference in the installation guide.

iso/live-build/config/hooks/live/99-configure-gnome.hook.chroot-75-97 (1)

75-97: Address icon theme inconsistency across branding configurations.

The background image path at lines 85-86, 91 (/usr/share/backgrounds/cortex/cortex-default.png) is properly installed—the file exists in iso/live-build/config/includes.chroot/usr/share/backgrounds/cortex/ and is verified by the 50-cortex-branding.hook.chroot hook.

However, icon theme inconsistency exists: Line 80 sets icon-theme='Adwaita' for the live user session, while branding/gdm/gdm-branding.conf specifies icon-theme='cortex-icons'. This creates a mismatch in icon themes across environments. The actual GDM hook (50-cortex-branding.hook.chroot) does not set an icon-theme for the login screen, leaving it unspecified. Clarify the intended icon theme strategy and update configurations consistently—either use cortex-icons throughout or document why different themes apply to different contexts.

Line 95's welcome-dialog-last-shown-version='9999999999' is functional but non-standard. Consider adding a comment explaining this approach.

iso/live-build/config/hooks/live/99-configure-gnome.hook.chroot-61-61 (1)

61-61: Remove unnecessary execute permission on .desktop file.

The execute permission is not required by the XDG/Freedesktop autostart specification for files in ~/.config/autostart. GNOME's autostart mechanism reads the .desktop file as configuration and executes the command specified in the Exec key regardless of filesystem permissions. The chmod +x line serves no purpose and should be removed.

Execute permissions are only needed if the file is intended to be double-clicked/launched from a file manager, which is not the case here.

iso/preseed/profiles/cortex-secops.preseed-41-41 (1)

41-41: Same hardcoded disk path issue as core profile.

See the comment on cortex-core.preseed regarding /dev/sda hardcoding.

Makefile-204-206 (1)

204-206: Silent failure on missing ISO output.

The mv ... || true pattern will silently succeed even if the ISO wasn't built, making it hard to detect build failures. Consider checking if the source file exists first or failing explicitly.

🐛 Proposed fix
 	@# Move output
-	@mv $(BUILD_DIR)/$(1)/live-image-$(ARCH).hybrid.iso \
-		$(OUTPUT_DIR)/$(ISO_NAME)-$(1)-$(ISO_VERSION)-$(ARCH).iso 2>/dev/null || true
+	@if [ -f "$(BUILD_DIR)/$(1)/live-image-$(ARCH).hybrid.iso" ]; then \
+		mv $(BUILD_DIR)/$(1)/live-image-$(ARCH).hybrid.iso \
+			$(OUTPUT_DIR)/$(ISO_NAME)-$(1)-$(ISO_VERSION)-$(ARCH).iso; \
+	else \
+		echo "ERROR: ISO not found at $(BUILD_DIR)/$(1)/live-image-$(ARCH).hybrid.iso"; \
+		exit 1; \
+	fi
 	@echo "ISO built: $(OUTPUT_DIR)/$(ISO_NAME)-$(1)-$(ISO_VERSION)-$(ARCH).iso"
branding/generate-assets.sh-403-403 (1)

403-403: Fix find command to count all image files correctly.

The -o operator has lower precedence than the implicit -print, so only .svg files will be counted. The .png files won't be included in the output.

🐛 Proposed fix
-find "${SCRIPT_DIR}" -name "*.png" -o -name "*.svg" | wc -l | xargs echo "  Total images:"
+find "${SCRIPT_DIR}" \( -name "*.png" -o -name "*.svg" \) | wc -l | xargs echo "  Total images:"
iso/live-build/config/hooks/live/50-cortex-branding.hook.chroot-101-104 (1)

101-104: Fix GRUB_DISTRIBUTOR configuration logic.

The sed -i command returns success (exit 0) even when the pattern isn't found, so the fallback echo will never execute. If GRUB_DISTRIBUTOR doesn't exist in the file, it won't be added. Use the same grep-then-sed-or-echo pattern used for GRUB_THEME above.

🐛 Proposed fix
-    # Also set timeout and other branding options
-    sed -i 's|^GRUB_DISTRIBUTOR=.*|GRUB_DISTRIBUTOR="Cortex Linux"|' /etc/default/grub || \
-        echo 'GRUB_DISTRIBUTOR="Cortex Linux"' >> /etc/default/grub
+    # Also set distributor branding
+    if grep -q "^GRUB_DISTRIBUTOR=" /etc/default/grub; then
+        sed -i 's|^GRUB_DISTRIBUTOR=.*|GRUB_DISTRIBUTOR="Cortex Linux"|' /etc/default/grub
+    else
+        echo 'GRUB_DISTRIBUTOR="Cortex Linux"' >> /etc/default/grub
+    fi
iso/preseed/profiles/cortex-core.preseed-41-41 (1)

41-41: Hardcoded disk path may cause installation failures.

Hardcoding /dev/sda will fail on systems with NVMe drives (/dev/nvme0n1), virtio disks (/dev/vda), or other storage configurations. Consider using an empty string to let the installer auto-detect, or documenting this as a template that must be customized.

♻️ Suggested alternative for auto-detection
 ### Partitioning - Simple UEFI (default for core)
 d-i partman-auto/method string regular
-d-i partman-auto/disk string /dev/sda
+# Leave blank for auto-detection, or set via boot parameter
+d-i partman-auto/disk string
iso/preseed/profiles/cortex-core.preseed-30-31 (1)

30-31: Password setup requires user action at first login; document security expectations clearly.

The preseed uses a placeholder password hash intentionally to force users to set their own credentials at first login, rather than shipping a pre-configured password. However, this design choice should be more explicitly documented in the preseed file itself—a comment like # IMPORTANT: User MUST set password on first login would prevent confusion about whether this is a misconfiguration or intentional.

Additionally, allow-password-weak boolean true (line 31) should be reconsidered for a server-focused core profile. Even with first-login setup, allowing weak passwords creates an exploitable window. Consider changing this to false and documenting the password requirements for users.

iso/live-build/config/includes.chroot/usr/share/plymouth/themes/cortex/cortex.script-123-129 (1)

123-129: Avoid leaking i into global scope in the bullets loop.
i isn’t declared local, so it may become global and collide with other loops/callbacks.

Proposed fix
 fun password_dialogue_opacity(opacity) {
+    local.i;
     if (global.password_entry && global.password_entry.sprite)
         global.password_entry.sprite.SetOpacity(opacity);
     if (global.password_prompt && global.password_prompt.sprite)
         global.password_prompt.sprite.SetOpacity(opacity);
     for (i = 0; global.password_bullets[i]; i++) {
         global.password_bullets[i].sprite.SetOpacity(opacity);
     }
 }
iso/live-build/config/includes.chroot/usr/share/plymouth/themes/cortex/cortex.script-73-96 (1)

73-96: Clamp progress values and clear progress bar on zero to avoid stale visuals.

The current code only updates the progress bar image when new_width > 1, leaving smaller values with the previously scaled image visible. Additionally, Scale() expects integer dimensions; floating-point values from out-of-range progress may cause unexpected behavior. Clamp progress to the expected range for each callback (0–1 for boot progress, 0–100 for system updates), convert to integer with Math.Int(), and use SetOpacity(0) to hide the bar when new_width <= 0.

Proposed fix
 fun progress_callback(duration, progress) {
     if (progress_bar.original_image.GetWidth() > 0) {
+        if (progress < 0) progress = 0;
+        if (progress > 1) progress = 1;
-        new_width = progress_bar.original_image.GetWidth() * progress;
-        if (new_width > 1) {
+        new_width = Math.Int(progress_bar.original_image.GetWidth() * progress);
+        if (new_width <= 0) {
+            progress_bar.sprite.SetOpacity(0);
+        } else {
+            progress_bar.sprite.SetOpacity(1);
             progress_bar.image = progress_bar.original_image.Scale(new_width, progress_bar.original_image.GetHeight());
             progress_bar.sprite.SetImage(progress_bar.image);
         }
     }
 }
@@
 fun system_update_callback(progress) {
     if (progress_bar.original_image.GetWidth() > 0) {
+        if (progress < 0) progress = 0;
+        if (progress > 100) progress = 100;
-        new_width = progress_bar.original_image.GetWidth() * progress / 100;
-        if (new_width > 1) {
+        new_width = Math.Int(progress_bar.original_image.GetWidth() * progress / 100);
+        if (new_width <= 0) {
+            progress_bar.sprite.SetOpacity(0);
+        } else {
+            progress_bar.sprite.SetOpacity(1);
             progress_bar.image = progress_bar.original_image.Scale(new_width, progress_bar.original_image.GetHeight());
             progress_bar.sprite.SetImage(progress_bar.image);
         }
     }
 }
🧹 Nitpick comments (36)
iso/live-build/config/includes.binary/boot/grub/themes/cortex/ASSETS.md (1)

1-53: Consider DRY principle: documentation duplication detected.

This file is identical to branding/grub/cortex/ASSETS.md. While the duplication may be intentional for different deployment contexts (binary ISO vs. branding source), it creates a maintenance burden where asset specifications must be kept synchronized manually.

Consider one of these approaches:

  • Use a single source of truth with symlinks
  • Generate this file from the canonical version during build
  • Add a comment referencing the canonical location if duplication is required
iso/live-build/config/includes.chroot/usr/share/plymouth/themes/cortex/ASSETS.md (1)

16-24: Add language specifier to color palette code block.

The code block lacks a language identifier, which affects rendering and syntax highlighting. Consider specifying a language for better documentation clarity.

📝 Proposed fix for markdown linting
 ## Color Palette
 
-```
+```text
 Primary Purple:   #6B21A8 (rgb: 107, 33, 168)
 Electric Cyan:    #06B6D4 (rgb: 6, 182, 212)
 Dark Background:  #0F0F23 (rgb: 15, 15, 35)
 Text Light:       #E2E8F0 (rgb: 226, 232, 240)
 Text Muted:       #94A3B8 (rgb: 148, 163, 184)
</details>

</blockquote></details>
<details>
<summary>iso/live-build/config/package-lists/live.list.chroot (1)</summary><blockquote>

`1-42`: **Confirm target Debian release + consider trimming size-heavy choices (`task-gnome-desktop`, `live-boot-doc`).**

- Please verify every package here exists in (and is appropriate for) the target Debian suite you’re building Cortex on; `task-gnome-desktop` in particular can pull a large dependency set and may make ISO size/control harder.  
- `live-boot-doc` is usually safe to drop from a production ISO unless you explicitly want offline docs.

 
<details>
<summary>Proposed diff (optional size trim)</summary>

```diff
--- a/iso/live-build/config/package-lists/live.list.chroot
+++ b/iso/live-build/config/package-lists/live.list.chroot
@@
 live-boot
-live-boot-doc
 initramfs-tools
branding/BRAND-GUIDELINES.md (1)

149-175: Fix markdownlint MD040 by adding languages to the fenced blocks (Buttons/Cards/Input Fields).

Proposed diff
--- a/branding/BRAND-GUIDELINES.md
+++ b/branding/BRAND-GUIDELINES.md
@@
 ### Buttons
-
-```
+```text
 Primary: #6B21A8 background, white text
 Secondary: transparent, #6B21A8 border
 Ghost: transparent, #94A3B8 text
 Danger: #EF4444 background, white text

@@

Cards

- +text
Background: #1E1E3F (dark) or #FFFFFF (light)
Border: 1px solid rgba(107, 33, 168, 0.2)
Border radius: 12px
Shadow: 0 4px 20px rgba(0, 0, 0, 0.15)

@@
### Input Fields
-
-```
+```text
Background: #0F0F23 (dark) or #F8FAFC (light)
Border: 1px solid #2D2D5A
Focus border: #06B6D4
Border radius: 8px
</details>

</blockquote></details>
<details>
<summary>iso/live-build/config/includes.chroot/boot/grub/themes/cortex/theme.txt (1)</summary><blockquote>

`4-10`: **Verify font support in your GRUB build (likely needs `.pf2` fonts + `loadfont`).**

Theme entries like `terminal-font: "DejaVu Sans Mono Regular 14"` and `item_font = "DejaVu Sans Regular 16"` only work if the corresponding GRUB font assets are present/loaded in your GRUB config.

 


Also applies to: 17-31

</blockquote></details>
<details>
<summary>branding/motd/10-cortex-sysinfo (1)</summary><blockquote>

`28-48`: **Optional: avoid calling `free`/`df` multiple times.**

Minor perf/cleanliness: you can capture `free` and `df` output once to avoid 3 subprocesses each (useful on slow systems and reduces MOTD latency).

</blockquote></details>
<details>
<summary>iso/preseed/partitioning/fde-uefi.preseed (1)</summary><blockquote>

`91-91`: **Duplicate directive: `weak_passphrase` already set on line 27.**

This directive is redundant and can be removed.


<details>
<summary>♻️ Proposed fix</summary>

```diff
-# Skip LUKS warning about weak passphrase (will be changed at first-boot)
-d-i partman-crypto/weak_passphrase boolean true
branding/motd/20-cortex-updates (1)

51-52: Minor style: Useless use of cat.

The cat is unnecessary here and in line 62.

♻️ Proposed fix
-        REBOOT_PKGS=$(cat /var/run/reboot-required.pkgs | head -3 | tr '\n' ', ' | sed 's/,$//')
+        REBOOT_PKGS=$(head -3 /var/run/reboot-required.pkgs | tr '\n' ', ' | sed 's/,$//')
-    cat "$CORTEX_NOTICE" | sed 's/^/    /'
+    sed 's/^/    /' "$CORTEX_NOTICE"
iso/preseed/partitioning/raid1-uefi.preseed (1)

30-31: Duplicate directive: new_vg_name set twice.

Lines 30 and 63 both set partman-auto-lvm/new_vg_name. Remove the duplicate.

Also applies to: 63-63

iso/provisioning/first-boot.sh (2)

61-72: Validate profile file content before sourcing.

The script sources ${PROFILE_FILE} directly without validating its contents. While the file should be root-owned, consider adding basic validation to ensure it only contains expected variable assignments.

♻️ Safer profile loading
 load_profile() {
     if [[ ! -f "${PROFILE_FILE}" ]]; then
         log_warn "Profile file not found, defaulting to 'core'"
         CORTEX_PROFILE="core"
     else
-        # shellcheck source=/dev/null
-        source "${PROFILE_FILE}"
+        # Only read CORTEX_PROFILE variable, ignore other content
+        CORTEX_PROFILE=$(grep -Po '^CORTEX_PROFILE=\K(core|full|secops)$' "${PROFILE_FILE}" 2>/dev/null || echo "core")
     fi
 
     log_info "Profile: ${CORTEX_PROFILE:-core}"
     export CORTEX_PROFILE="${CORTEX_PROFILE:-core}"
 }

74-83: Consider alternative network checks for restricted environments.

Pinging 8.8.8.8 may fail in environments where ICMP is blocked. Consider falling back to DNS resolution or HTTP connectivity checks.

♻️ More robust network check
 check_network() {
-    if ping -c 1 -W 3 8.8.8.8 &>/dev/null; then
+    if ping -c 1 -W 3 8.8.8.8 &>/dev/null || \
+       curl -s --max-time 3 -o /dev/null https://apt.cortexlinux.com &>/dev/null; then
         log_info "Network: Online"
         export NETWORK_AVAILABLE=true
     else
iso/live-build/config/includes.chroot/usr/share/plymouth/themes/cortex/cortex.plymouth (1)

1-8: Consider DRY: duplicate Plymouth theme configuration.

This file is identical to branding/plymouth/cortex/cortex.plymouth. While having separate copies for the branding package and live-build is a valid deployment strategy, consider symlinking or copying from a single source during build to avoid drift.

iso/live-build/config/bootloaders/grub-pc/live-theme/theme.txt (1)

12-25: Remove duplicate selected_item_color property.

selected_item_color is defined twice (lines 20 and 24) with the same value. Remove the duplicate to improve maintainability.

Suggested fix
 + boot_menu {
     left = 15%
     top = 25%
     width = 70%
     height = 50%
     item_font = "DejaVu Sans Regular 14"
     item_color = "#E2E8F0"
     selected_item_font = "DejaVu Sans Bold 14"
     selected_item_color = "#06B6D4"
     item_height = 36
     item_padding = 12
     item_spacing = 4
-    selected_item_color = "#06B6D4"
 }
branding/motd/00-cortex-banner (1)

7-27: Prefer printf over echo -e (and drop/justify unused color vars).
echo -e behavior can vary; printf is deterministic. Also PURPLE/CYAN/WHITE are currently unused (Lines 8-10).

Proposed change
-echo -e "${GRAY}                              L I N U X${RESET}"
-echo ""
+printf '%b\n\n' "${GRAY}                              L I N U X${RESET}"
docs/DEVELOPERS.md (1)

145-150: Verify QEMU testing commands are compatible with build output.

The QEMU testing commands (lines 145-150) assume the build process generates ISO files in output/ with the expected naming scheme. Ensure:

  1. The ISO naming matches the pattern cortex-linux-full-*.iso
  2. The wildcard expansion will work correctly in shell commands
  3. QEMU paths are correct for common Linux distributions

Note: These are illustrative examples, but developers may copy them verbatim.

Consider making the QEMU examples more robust:

Optional improvement: More robust QEMU examples
  ### Test ISO in VM

  ```bash
-# QEMU (amd64)
-qemu-system-x86_64 -m 4G -cdrom output/cortex-linux-full-*.iso -boot d
-
-# QEMU (arm64)
-qemu-system-aarch64 -M virt -cpu cortex-a72 -m 4G -cdrom output/cortex-linux-full-*-arm64.iso
+# QEMU (amd64)
+ISO=$(ls output/cortex-linux-full-*-amd64.iso 2>/dev/null | tail -1)
+qemu-system-x86_64 -m 4G -cdrom "$ISO" -boot d
+
+# QEMU (arm64)
+ISO=$(ls output/cortex-linux-full-*-arm64.iso 2>/dev/null | tail -1)
+qemu-system-aarch64 -M virt -cpu cortex-a72 -m 4G -cdrom "$ISO"
</details>

</blockquote></details>
<details>
<summary>branding/wallpapers/ASSETS.md (1)</summary><blockquote>

`45-77`: **Design guidelines are well-documented, but implementation needs verification.**

The design guidelines for each of the 6 wallpaper themes are detailed and provide clear visual direction:
- Neural Dark/Light with network patterns
- Gradient Purple/Cyan variations
- Minimal Dark with logo
- Circuit Board tech aesthetic

These provide good guidance for designers, but confirm:
1. The actual asset files match these design specifications
2. The design is cohesive and professional across all themes
3. Colors and patterns are rendered correctly at all specified resolutions



Consider adding design asset sources or generation instructions if wallpapers are being generated programmatically (e.g., via `branding/generate-assets.sh` mentioned in AI summary). This would help future maintainers reproduce or modify the wallpapers.

<details>
<summary>Optional enhancement: Document wallpaper generation</summary>

If wallpapers are generated (not hand-created), document:

```markdown
## Generation

Wallpapers can be generated using the `branding/generate-assets.sh` script:

\`\`\`bash
./branding/generate-assets.sh --wallpapers
\`\`\`

This regenerates all wallpapers at the specified resolutions.
branding/gdm/INSTALL.md (1)

16-37: Method 1: verify that update-alternatives on gnome-shell.css actually affects GDM on the target GNOME Shell build.
On many distros the greeter theme is effectively driven by the compiled gnome-shell-theme.gresource; if GDM doesn’t read /usr/share/gnome-shell/theme/gnome-shell.css, this method won’t apply (or will apply inconsistently). If it does work for your target, consider adding the missing “select it” step so users don’t forget.

Proposed doc tweak
 sudo update-alternatives --install \
     /usr/share/gnome-shell/theme/gnome-shell.css \
     gdm-theme /usr/share/gnome-shell/theme/Cortex/gnome-shell.css 100
+
+# Select the alternative (if multiple are installed)
+sudo update-alternatives --config gdm-theme
packages/cortex-secops/DEBIAN/control (1)

1-6: Consider version-pinning the bundle to the matching cortex-core version.
For metapackages, Depends: cortex-core (= ${binary:Version}) (or at least >=) helps prevent mixed-version installs when repos lag or users partially upgrade.

Also applies to: 38-55

iso/preseed/profiles/cortex-full.preseed (1)

140-148: Late-command: add systemctl daemon-reload before enabling the new unit.
Enabling a freshly-copied unit can fail if systemd hasn’t noticed it yet.

Proposed change
 d-i preseed/late_command string \
     in-target mkdir -p /opt/cortex/provisioning; \
     in-target mkdir -p /var/log/cortex; \
     cp /cdrom/provisioning/first-boot.sh /target/opt/cortex/provisioning/; \
     cp /cdrom/provisioning/cortex-first-boot.service /target/etc/systemd/system/; \
     echo "CORTEX_PROFILE=full" > /target/opt/cortex/provisioning/profile; \
     in-target chmod +x /opt/cortex/provisioning/first-boot.sh; \
+    in-target systemctl daemon-reload; \
     in-target systemctl enable cortex-first-boot.service
iso/preseed/partitioning/simple-bios.preseed (1)

17-37: Consider documenting the absence of swap or making it configurable.

The partition recipe defines no swap space, which may be intentional for modern systems with adequate RAM or container/cloud deployments. However, many traditional installations benefit from swap (especially for hibernation, memory-constrained systems, or swap-on-zram).

📋 Optional: Add a swap partition or document the rationale

If swap should be included, add a swap partition entry before the root partition:

               .                                               \
+              2048 4096 8192 linux-swap                        \
+                      $primary{ }                             \
+                      method{ swap } format{ }                \
+              .                                               \
               8192 16384 -1 ext4                               \

Alternatively, add a comment explaining why swap is omitted:

 # Define partition recipe
+# Note: No swap partition - assumes adequate RAM or swap-on-zram at runtime
 d-i partman-auto/expert_recipe string                         \
docs/QUICK-START.md (1)

42-48: Consider increasing disk recommendation for cortex-full.

The 20GB disk requirement for cortex-full might be insufficient for active development. Docker images, Node.js node_modules, Go/Rust build artifacts, and development projects can quickly consume space. Consider recommending 40GB+ for comfortable development use.

 | Profile | RAM | Disk | Use Case |
 |---------|-----|------|----------|
 | cortex-core | 512MB | 2GB | Servers, embedded, minimal |
-| cortex-full | 4GB | 20GB | Workstations, development |
+| cortex-full | 4GB | 40GB | Workstations, development |
 | cortex-secops | 4GB | 20GB | Security-critical systems |
iso/preseed/cortex-base.preseed (1)

60-77: Package selection is well-curated for a minimal server install.

The included packages balance essential tools (ssh, sudo, curl, git, firewall) with a minimal footprint. This is a solid foundation for base installations.

✓ Security tools (sudo, ufw, fail2ban, ca-certificates, gnupg) included.
✓ Admin/dev tools (vim, htop, python3, git) included.
✓ No unnecessary bloat (no X11, no heavy applications).

If this base is intended for minimal/core profiles, consider whether to add grub-pc or grub-efi explicitly (they should be pulled in by task selection, but explicit inclusion reduces surprises).

iso/live-build/config/hooks/live/99-configure-gnome.hook.chroot (2)

102-123: Consider avoiding blanket error suppression on schema compilation.

Line 123 suppresses all errors from glib-compile-schemas with || true. While this prevents build failures, it may hide genuine schema syntax errors or permission issues.

Consider logging errors for debugging:

♻️ Proposed refinement
 # Compile schemas
-glib-compile-schemas /usr/share/glib-2.0/schemas/ 2>/dev/null || true
+if ! glib-compile-schemas /usr/share/glib-2.0/schemas/ 2>&1; then
+  echo "Warning: glib-compile-schemas failed, but continuing..." >&2
+fi

25-40: Remove redundant GDM configuration file.

/etc/gdm3/daemon.conf and /etc/gdm3/custom.conf serve the same purpose and contain identical settings. GDM3 reads only one system override file per distribution; custom.conf is the standard and recommended file across modern GDM3 versions. Keep only custom.conf and remove the daemon.conf creation to avoid unnecessary duplication.

branding/grub/cortex/theme.txt (1)

46-56: Hardcoded version string requires manual maintenance.

Line 52 contains the hardcoded version string "v1.0 Synapse". This will require manual updates for each new release. Consider:

  1. Documenting this as a required update step in release procedures, or
  2. Generating this file dynamically during the build process using version information from branding/os-release/os-release.
docs/automated-installation.md (1)

36-38: Add language specifier to fenced code blocks for better rendering.

Per static analysis hints, several code blocks are missing language specifiers (lines 36, 140, 185, 207, 310). Adding language identifiers improves syntax highlighting and accessibility.

Example fix
-```
+```text
 preseed/file=/cdrom/preseed/profiles/cortex-{profile}.preseed
branding/install-branding.sh (2)

152-155: Handle empty glob safely to avoid unexpected behavior.

If /etc/update-motd.d/ is empty or doesn't exist, the glob * won't expand and the loop will iterate once with the literal string *, causing the [ -f "$f" ] check to fail unexpectedly. While this won't break the script due to || true, it's cleaner to handle explicitly.

Suggested fix
     # Disable existing MOTD scripts
-    for f in /etc/update-motd.d/*; do
-        [ -f "$f" ] && chmod -x "$f" 2>/dev/null || true
-    done
+    if [ -d /etc/update-motd.d ]; then
+        for f in /etc/update-motd.d/*; do
+            [ -f "$f" ] && chmod -x "$f" 2>/dev/null || true
+        done
+    fi

131-134: Clarify GDM detection logic.

The condition ! command -v gdm3 &>/dev/null && ! [ -d /etc/gdm3 ] returns true when both the command is missing and the directory doesn't exist. This is correct for an OR-skip scenario due to De Morgan's law, but could be clearer for maintainability.

Consider using explicit OR logic:

Suggested refactor
-    if ! command -v gdm3 &>/dev/null && ! [ -d /etc/gdm3 ]; then
+    if ! command -v gdm3 &>/dev/null && ! [ -d /etc/gdm3 ]; then
+        # Neither gdm3 command nor config directory exists
         log_warn "GDM not found, skipping"
         return
     fi

Actually, the logic appears intentional - skip only if both are absent. Adding a comment would improve clarity.

packages/cortex-branding/DEBIAN/control (1)

6-8: Consider making plymouth a Recommends instead of Depends.

The package's postinst and prerm scripts already gracefully handle systems without Plymouth (using if command -v checks), and no content files require Plymouth to be present. Moving plymouth and plymouth-themes to Recommends would allow users to install cortex-branding for other features (OS identity, GRUB theme, wallpapers, GDM styling, MOTD) without pulling in unnecessary graphics dependencies on headless systems.

Note: The current architecture already mitigates this concern—cortex-core (the minimal profile) doesn't depend on cortex-branding at all, while cortex-full (desktop) includes both. This suggestion would add flexibility for users who want branding components without the boot splash.

Suggested change
-Depends: plymouth, plymouth-themes
+Depends:
+Recommends: plymouth, plymouth-themes, gnome-shell, gdm3
-Recommends: gnome-shell, gdm3
 Suggests: neofetch
iso/live-build/config/hooks/live/50-cortex-branding.hook.chroot (2)

55-61: Add timeout and retry logic for network operation.

The curl command to download the GPG key lacks timeout options and retry logic. During ISO build, network issues could cause the build to hang indefinitely or fail unpredictably.

♻️ Suggested improvement
 # Download and install GPG key
 mkdir -p /usr/share/keyrings
-curl -fsSL https://apt.cortexlinux.com/keys/cortex-linux.gpg.asc | gpg --dearmor -o /usr/share/keyrings/cortex-linux.gpg
+curl --retry 3 --retry-delay 5 --max-time 30 -fsSL https://apt.cortexlinux.com/keys/cortex-linux.gpg.asc | gpg --dearmor -o /usr/share/keyrings/cortex-linux.gpg

196-196: Nitpick: Avoid useless use of cat.

The cat /proc/loadavg | awk pattern can be simplified.

♻️ Suggested fix
-LOAD=$(cat /proc/loadavg | awk '{print $1, $2, $3}')
+LOAD=$(awk '{print $1, $2, $3}' /proc/loadavg)
branding/generate-assets.sh (1)

18-18: Remove unused variable.

LIGHT_CYAN is defined but never used in the script, as flagged by shellcheck.

♻️ Suggested fix
 ELECTRIC_CYAN="#06B6D4"
-LIGHT_CYAN="#22D3EE"
 DARK_BG="#0F0F23"
branding/plymouth/cortex/cortex.script (1)

139-146: Message sprites may accumulate without cleanup.

Each call to message_callback creates a new sprite without removing the previous one. While this is unlikely to cause issues during a typical boot sequence, rapid message updates could lead to visual artifacts or memory accumulation.

♻️ Suggested fix to reuse message sprite
+// Initialize message sprite once
+global.message_sprite = Sprite();
+global.message_sprite.SetOpacity(0);
+
 // Message display
 fun message_callback(text) {
     message.image = Image.Text(text, 0.6, 0.6, 0.7, 1, "Sans 11");
-    message.sprite = Sprite(message.image);
+    global.message_sprite.SetImage(message.image);
     message.x = screen_x + screen_width / 2 - message.image.GetWidth() / 2;
     message.y = screen_y + screen_height - 50;
-    message.sprite.SetPosition(message.x, message.y, 10);
+    global.message_sprite.SetPosition(message.x, message.y, 10);
+    global.message_sprite.SetOpacity(1);
 }
Makefile (2)

227-237: ARM64 targets run validation twice.

The ARM64 targets have check-deps validate as prerequisites, but then call $(MAKE) ARCH=arm64 iso-<profile> which also has these prerequisites. This causes redundant validation runs.

♻️ Suggested simplification
 # ARM64 ISO targets
 iso-arm64: iso-arm64-full

-iso-arm64-core: check-deps validate $(BUILD_DIR) $(OUTPUT_DIR)
+iso-arm64-core:
 	$(MAKE) ARCH=arm64 iso-core

-iso-arm64-full: check-deps validate $(BUILD_DIR) $(OUTPUT_DIR)
+iso-arm64-full:
 	$(MAKE) ARCH=arm64 iso-full

-iso-arm64-secops: check-deps validate $(BUILD_DIR) $(OUTPUT_DIR)
+iso-arm64-secops:
 	$(MAKE) ARCH=arm64 iso-secops

367-373: Branding package may be incomplete due to silent copy failures.

The cp ... || true patterns on lines 367-373 will silently skip missing source files, potentially producing an incomplete .deb package. Consider adding validation or removing the || true to catch missing assets.

♻️ Suggested improvement to validate sources
+	@# Validate required assets exist
+	@test -f $(BRANDING_DIR)/plymouth/cortex/cortex.plymouth || { echo "ERROR: Plymouth theme missing"; exit 1; }
+	@test -f $(BRANDING_DIR)/grub/cortex/theme.txt || { echo "ERROR: GRUB theme missing"; exit 1; }
 	@# Copy Plymouth theme
-	@cp $(BRANDING_DIR)/plymouth/cortex/* $(BUILD_DIR)/cortex-branding/usr/share/plymouth/themes/cortex/ 2>/dev/null || true
+	@cp $(BRANDING_DIR)/plymouth/cortex/* $(BUILD_DIR)/cortex-branding/usr/share/plymouth/themes/cortex/
 	@# Copy GRUB theme
-	@cp $(BRANDING_DIR)/grub/cortex/* $(BUILD_DIR)/cortex-branding/boot/grub/themes/cortex/ 2>/dev/null || true
+	@cp $(BRANDING_DIR)/grub/cortex/* $(BUILD_DIR)/cortex-branding/boot/grub/themes/cortex/
iso/live-build/config/includes.chroot/usr/share/plymouth/themes/cortex/cortex.script (1)

55-71: Pulse animation is coupled to progress updates (may "freeze" early).
pulse_logo() is only called from progress_callback(). If boot progress updates are infrequent, the pulse won't animate. Additionally, global.progress is declared but never used.

Consider decoupling the animation using Plymouth.SetRefreshFunction(), which is the supported mechanism for independent frame-based animations in Plymouth and runs up to 50 times per second:

Proposed fix (decouple animation from progress)
 // Animation variables
 global.progress = 0;
 global.pulse_opacity = 1;
 global.pulse_direction = -1;

+fun refresh_callback() {
+    pulse_logo();
+}
+Plymouth.SetRefreshFunction(refresh_callback);
+
 // Progress callback
 fun progress_callback(duration, progress) {
+    global.progress = progress;
     if (progress_bar.original_image.GetWidth() > 0) {
         new_width = progress_bar.original_image.GetWidth() * progress;
@@
-    pulse_logo();
 }
 Plymouth.SetBootProgressFunction(progress_callback);

This applies to progress_callback() and system_update_callback() patterns.

Comment on lines +1 to +8
# Cortex Linux GDM Configuration
#
# This file configures GDM branding settings.
# Place in /etc/gdm3/greeter.dconf-defaults

[org/gnome/login-screen]
# Logo displayed on login screen
logo='/usr/share/cortex/logos/cortex-logo-light.svg'
Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Potential issue | 🔴 Critical

🧩 Analysis chain

🏁 Script executed:

#!/bin/bash
# Search for cortex logo files in branding
echo "=== Searching for cortex logo files ==="
fd -t f "cortex-logo" branding/

echo -e "\n=== Listing branding directory structure ==="
find branding/ -type f -name "*.svg" -o -name "*.png" | head -20

echo -e "\n=== Checking for installation scripts/hooks ==="
fd -t f "(install|hook|spec|debian|postinst)" branding/ | head -20

echo -e "\n=== Checking gdm-branding.conf directory ==="
ls -la branding/gdm/

Repository: cortexlinux/cortex-distro

Length of output: 1629


🏁 Script executed:

#!/bin/bash
# Check installation script and documentation
echo "=== install-branding.sh content ==="
cat branding/install-branding.sh

echo -e "\n=== INSTALL.md content ==="
cat branding/gdm/INSTALL.md

Repository: cortexlinux/cortex-distro

Length of output: 9247


Fix logo path and filename in GDM configuration.

Line 8 references /usr/share/cortex/logos/cortex-logo-light.svg, but this file doesn't exist in the branding assets. The actual logo file is branding/gdm/cortex-logo.svg (without the "-light" suffix), and the install-branding.sh script doesn't copy any logo to /usr/share/cortex/logos/. Update the configuration to use the correct filename and ensure the logo asset is properly installed to the referenced path, or revise the installation script to copy the logo file appropriately.

🤖 Prompt for AI Agents
In @branding/gdm/gdm-branding.conf around lines 1 - 8, The GDM config references
a non-existent logo path '/usr/share/cortex/logos/cortex-logo-light.svg'; update
the login-screen logo setting to the actual filename 'cortex-logo.svg' and
ensure the asset is installed to that path by either modifying the
install-branding.sh script to copy 'branding/gdm/cortex-logo.svg' into
'/usr/share/cortex/logos/' (creating the directory if needed) or change the
config to point to wherever install-branding.sh currently places the logo;
update the value in [org/gnome/login-screen] (logo=) and adjust
install-branding.sh accordingly so the file and path match.

Comment on lines +20 to +28
[org/gnome/desktop/interface]
# Set GTK theme for login screen
gtk-theme='Adwaita-dark'

# Icon theme
icon-theme='cortex-icons'

# Cursor theme
cursor-theme='Adwaita'
Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Potential issue | 🔴 Critical

🧩 Analysis chain

🏁 Script executed:

#!/bin/bash
# Search for cortex-icons theme references and installation
echo "=== Searching for cortex-icons references ==="
fd -type f -name "*cortex-icons*" 2>/dev/null || echo "No cortex-icons files found by name"
rg -l "cortex-icons" --type conf --type hook 2>/dev/null || echo "No cortex-icons in config/hook files"

echo -e "\n=== Searching for gnome hook configuration ==="
fd -type f -name "*configure-gnome*" 2>/dev/null
fd -type f -name "*.hook.chroot" 2>/dev/null | head -20

echo -e "\n=== Searching for icon-theme references ==="
rg "icon-theme" --type conf --type hook -n 2>/dev/null

Repository: cortexlinux/cortex-distro

Length of output: 280


🏁 Script executed:

#!/bin/bash
echo "=== Repository structure ==="
git ls-files | head -30

echo -e "\n=== Searching branding directory ==="
fd -type f . branding/ 2>/dev/null | head -20

echo -e "\n=== All hook files ==="
fd -type f -name "*.hook*" 2>/dev/null

echo -e "\n=== Searching for gnome in all files ==="
rg "gnome|GNOME" -l --max-count 3 2>/dev/null | head -20

echo -e "\n=== All icon-theme references globally ==="
rg "icon-theme" -n 2>/dev/null

Repository: cortexlinux/cortex-distro

Length of output: 2154


🏁 Script executed:

#!/bin/bash
echo "=== Contents of branding/gdm/gdm-branding.conf ==="
cat -n branding/gdm/gdm-branding.conf

echo -e "\n=== Contents of iso/live-build/config/hooks/live/99-configure-gnome.hook.chroot (lines 70-115) ==="
sed -n '70,115p' iso/live-build/config/hooks/live/99-configure-gnome.hook.chroot

echo -e "\n=== Search for cortex-icons package definition ==="
fd -type f "cortex-icons|cortex-branding" packages/ 2>/dev/null
find packages -type f -name "control" -o -name "*.list" | xargs grep -l "cortex-icons" 2>/dev/null

echo -e "\n=== Check if cortex-icons is mentioned in any package lists ==="
rg "cortex-icons|icon.*theme" packages/ iso/preseed/ 2>/dev/null

Repository: cortexlinux/cortex-distro

Length of output: 3056


🏁 Script executed:

#!/bin/bash
echo "=== Search for any cortex-icons definition or theme files ==="
find . -type d -name "cortex-icons" 2>/dev/null
rg "cortex-icons" -n 2>/dev/null

echo -e "\n=== Check if cortex-icons is in any asset/installation scripts ==="
cat branding/install-branding.sh | head -50

echo -e "\n=== Check GDM install documentation ==="
cat branding/gdm/INSTALL.md

Repository: cortexlinux/cortex-distro

Length of output: 4244


Remove 'cortex-icons' reference or implement the icon theme.

Line 25 specifies icon-theme='cortex-icons', which does not appear to be defined or installed anywhere in the codebase. The live GNOME environment (iso/live-build/config/hooks/live/99-configure-gnome.hook.chroot lines 80, 107) uses icon-theme='Adwaita' instead.

GDM will fail to apply the cortex-icons theme, falling back to defaults and breaking the intended login screen branding. Either implement the cortex-icons theme and install it in both configurations, or align both GDM and the live session to use the same icon theme (e.g., Adwaita).

🤖 Prompt for AI Agents
In @branding/gdm/gdm-branding.conf around lines 20 - 28, The GDM config sets
icon-theme='cortex-icons' in the [org/gnome/desktop/interface] section but that
theme isn’t provided; either remove that cortex-icons entry or install/ship the
cortex-icons theme and ensure it’s referenced identically in the live session
hooks (the live/99-configure-gnome.hook.chroot usage that currently uses
icon-theme='Adwaita'); update the GDM key icon-theme='cortex-icons' to
icon-theme='Adwaita' if you prefer to align with the live session, or add the
cortex-icons theme package and update both GDM and live session hooks to use it.

Comment on lines 9 to 15
```bash
# Debian/Ubuntu
sudo apt update
sudo apt install live-build gpg python3 shellcheck debootstrap

# Verify live-build version (need >= 1:20210814)
dpkg -l live-build
Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Potential issue | 🔴 Critical

🧩 Analysis chain

🌐 Web query:

live-build version 1:20210814 Debian Ubuntu 2025 2026 repository support

💡 Result:

Short answer: live-build version "1:20210814" is not present in current Debian or Ubuntu archives — Debian packages in stable/testing/unstable use other timestamps (examples: 1:20210407 in bullseye, 1:20230502 in bookworm, 1:20250505 in trixie and 1:20250814 in forky/sid), and Ubuntu publishes live-build as the 3.0~a57... series. See Debian and Ubuntu package pages below for details. [1][2][3]

Sources:

  • Debian live-build package listings (bullseye/bookworm/trixie/forky/sid). [1]
  • Debian live-build (source in trixie). [2]
  • Ubuntu / Launchpad live-build package versions (jammy/noble). [3]

🌐 Web query:

live-build latest stable version Debian bullseye bookworm trixie 2025 2026

💡 Result:

Summary (live-build package versions in Debian suites):

  • Trixie (stable): 1:20250505+deb13u1. [1][2]
  • Bookworm (oldstable): 1:20230502. [1][3]
  • Bullseye (oldoldstable): 1:20210407. [1][4]

Source: Debian package pages for live-build. [1][2][3][4]


🌐 Web query:

live-build package version history Debian live-build >= 1.20210814

💡 Result:

Here are Debian live-build source-package versions (chronological, versions >= 20210814) with Debian references:

  • 1:20220505 — appeared in testing (after 2021). [1]
  • 1:20230131 — migrated to testing Feb 2023. [2]
  • 1:20230502 — migrated to testing May 2023 / in bookworm. [1][3]
  • 1:20240810 — (found in bug reports / tracker as a later release). [4]
  • 1:20250225 — later unstable/testing snapshot (seen in bug tracker). [4]
  • 1:20250814 — current unstable/forky/sid source package (Debian VCS / package page). [1][5]

Sources:

  1. packages.debian.org — live-build package listings (versions by suite). [packages.debian.org live-build]. [1]
  2. Debian package tracker / news (migration 2023-02-03: 1:20230131). [tracker.debian.org]. [2]
  3. PTS / news (1:20230502 migrated to testing). [packages.qa.debian.org]. [3]
  4. Debian Bug tracker / package news entries citing versions 20240810, 20250225. [bugs.debian.org / tracker]. [4]
  5. live-build source package page (sid) and Salsa git (1:20250814). [packages.debian.org source live-build]. [5]

If you want a full changelog (full entries/dates) I can fetch the Debian changelog file (debian/changelog) and paste the entries for each version.


The version requirement live-build >= 1:20210814 does not exist in any Debian or Ubuntu repository.

The version "1:20210814" is not published in Debian (bullseye, bookworm, trixie, or sid) or Ubuntu archives. Current available versions are: 1:20210407 (bullseye), 1:20230502 (bookworm), and 1:20250505 (trixie).

Update the version requirement to a valid version that exists in the target distribution. If bullseye support is not required, change this to live-build >= 1:20230502 (bookworm+) or the appropriate version for your minimum supported Debian release.

🤖 Prompt for AI Agents
In @docs/DEVELOPERS.md around lines 9 - 15, The documented live-build version
requirement is invalid; update the version string in the DEVELOPER doc block
where the live-build install instructions and the "Verify live-build version"
check appear so it references a real release for your supported distro (e.g.,
change "live-build >= 1:20210814" to a valid version such as "live-build >=
1:20230502" for bookworm+ or to the specific minimum available in your
minimum-supported release); ensure the comment and the dpkg verification line
reflect the chosen valid version.

Comment on lines +54 to +63
```bash
# System upgrade with rollback support
cortex-upgrade --check

# GPU detection and driver status
cortex-gpu status

# Verify system integrity
cortex-verify --quick
```
Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Potential issue | 🔴 Critical

🧩 Analysis chain

🏁 Script executed:

#!/bin/bash
# Find tool implementations
echo "=== Searching for tool implementations ==="
find . -type f -name "*cortex*" | grep -E "(cortex-upgrade|cortex-gpu|cortex-verify)" | head -20
find . -type f \( -name "*.py" -o -name "*.js" -o -name "*.ts" -o -name "*.sh" \) | xargs grep -l "cortex-upgrade\|cortex-gpu\|cortex-verify" 2>/dev/null | head -20

Repository: cortexlinux/cortex-distro

Length of output: 347


🏁 Script executed:

#!/bin/bash
# Search for CLI argument parsing for these tools
echo "=== Searching for argument/flag definitions ==="
rg "add_argument.*--check|argparse|click.*--check" -t py -t js -A 2 | head -50
rg "cortex-upgrade|cortex-gpu|cortex-verify" -t py -A 5 | head -100

Repository: cortexlinux/cortex-distro

Length of output: 4796


🏁 Script executed:

#!/bin/bash
# Look for main entry points and CLI definitions
fd -t f "main.py\|cli.py\|index.js\|app.py" | xargs grep -l "cortex-upgrade\|cortex-gpu\|cortex-verify" 2>/dev/null
ls -la . | head -20
git ls-files | grep -E "cortex|cli|main" | head -30

Repository: cortexlinux/cortex-distro

Length of output: 2189


🏁 Script executed:

#!/bin/bash
# Read cortex-upgrade.py to check all arguments
echo "=== cortex-upgrade.py arguments ==="
cat packages/cortex-core/cortex-upgrade.py | grep -A 50 "parser.add_argument"

Repository: cortexlinux/cortex-distro

Length of output: 2414


🏁 Script executed:

#!/bin/bash
# Read cortex-gpu.py to check all arguments
echo "=== cortex-gpu.py arguments and subcommands ==="
cat packages/cortex-core/cortex-gpu.py | grep -A 50 "parser.add_argument\|subparsers\|add_parser"

Repository: cortexlinux/cortex-distro

Length of output: 3027


🏁 Script executed:

#!/bin/bash
# Read cortex-verify.py to check all arguments
echo "=== cortex-verify.py arguments ==="
cat packages/cortex-core/cortex-verify.py | grep -A 30 "parser.add_argument"

Repository: cortexlinux/cortex-distro

Length of output: 1299


Update command examples to match actual tool interfaces.

The documentation contains inaccurate command flags:

  • cortex-upgrade --check does not exist; use cortex-upgrade plan instead (commands: plan, execute, rollback, status)
  • cortex-verify --quick does not exist; use cortex-verify to run all checks or add flags like --json for output formatting
  • cortex-gpu status is correct and supported
🤖 Prompt for AI Agents
In @docs/QUICK-START.md around lines 54 - 63, The example commands in the docs
use incorrect flags; replace `cortex-upgrade --check` with the supported
`cortex-upgrade plan` (and mention the valid subcommands: plan, execute,
rollback, status) and change `cortex-verify --quick` to just `cortex-verify` (or
note supported output flags like `--json`) while leaving `cortex-gpu status`
as-is; update the README examples and any occurrences of `cortex-upgrade
--check` and `cortex-verify --quick` to the correct command forms
(`cortex-upgrade plan` and `cortex-verify`) and optionally add a brief note
about `--json` output for `cortex-verify`.

Comment on lines 10 to 25
# Set Plymouth theme
if command -v plymouth-set-default-theme &>/dev/null; then
plymouth-set-default-theme cortex || true
update-initramfs -u || true
fi

# Update GRUB theme
if [ -f /etc/default/grub ]; then
if ! grep -q "GRUB_THEME=" /etc/default/grub; then
echo 'GRUB_THEME="/boot/grub/themes/cortex/theme.txt"' >> /etc/default/grub
else
sed -i 's|^GRUB_THEME=.*|GRUB_THEME="/boot/grub/themes/cortex/theme.txt"|' /etc/default/grub
fi
update-grub 2>/dev/null || true
fi

Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Potential issue | 🔴 Critical

🧩 Analysis chain

🏁 Script executed:

find packages/cortex-branding/DEBIAN -type f

Repository: cortexlinux/cortex-distro

Length of output: 187


🏁 Script executed:

cat -n packages/cortex-branding/DEBIAN/postinst

Repository: cortexlinux/cortex-distro

Length of output: 2625


🏁 Script executed:

[ -f packages/cortex-branding/DEBIAN/prerm ] && cat -n packages/cortex-branding/DEBIAN/prerm || echo "No prerm script found"

Repository: cortexlinux/cortex-distro

Length of output: 942


🏁 Script executed:

[ -f packages/cortex-branding/DEBIAN/postrm ] && cat -n packages/cortex-branding/DEBIAN/postrm || echo "No postrm script found"

Repository: cortexlinux/cortex-distro

Length of output: 91


GRUB_THEME not restored on package removal — leaves broken reference pointing to deleted file.

The postinst script mutates /etc/default/grub (lines 17-22) but prerm does not restore it. When the package is removed, GRUB_THEME="/boot/grub/themes/cortex/theme.txt" remains in the file even though the directory no longer exists, causing boot-time failures or GRUB warnings.

Either save the previous GRUB_THEME value to /var/lib/cortex-branding/grub-theme.prev in postinst and restore it in prerm, or remove the GRUB_THEME line entirely during package removal.

Note: Plymouth theme restoration is already correctly handled via plymouth-set-default-theme -R in prerm (line 12).

🤖 Prompt for AI Agents
In @packages/cortex-branding/DEBIAN/postinst around lines 10 - 25, Postinst sets
GRUB_THEME in /etc/default/grub but prerm does not restore it, leaving a broken
GRUB_THEME pointing to a removed file; modify postinst to save the previous
GRUB_THEME value (or empty marker) to /var/lib/cortex-branding/grub-theme.prev
before writing the new GRUB_THEME and modify prerm to read
/var/lib/cortex-branding/grub-theme.prev and either restore the original
GRUB_THEME line in /etc/default/grub (if a previous value exists) or remove the
GRUB_THEME line entirely (if marker indicates none), then run update-grub (or
update-grub 2>/dev/null || true) to apply changes; reference the scripts
postinst and prerm, the GRUB_THEME entry in /etc/default/grub, and the state
file /var/lib/cortex-branding/grub-theme.prev when implementing.

Comment on lines 6 to 21
set -e

case "$1" in
remove|upgrade|deconfigure)
# Restore default Plymouth theme
if command -v plymouth-set-default-theme &>/dev/null; then
plymouth-set-default-theme -R 2>/dev/null || true
fi

# Re-enable default MOTD scripts
if [ -d /etc/update-motd.d ]; then
for f in /etc/update-motd.d/??-* ; do
chmod +x "$f" 2>/dev/null || true
done
fi
;;
Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Potential issue | 🔴 Critical

🧩 Analysis chain

🏁 Script executed:

# Find the Debian package files related to cortex-branding
find packages/cortex-branding/DEBIAN -type f | head -20

Repository: cortexlinux/cortex-distro

Length of output: 187


🏁 Script executed:

# Check the postinst script to see what state is actually persisted
if [ -f packages/cortex-branding/DEBIAN/postinst ]; then
    cat -n packages/cortex-branding/DEBIAN/postinst
fi

Repository: cortexlinux/cortex-distro

Length of output: 2625


🏁 Script executed:

# Check for any other relevant scripts (preinst, postrm)
ls -la packages/cortex-branding/DEBIAN/

Repository: cortexlinux/cortex-distro

Length of output: 350


🌐 Web query:

plymouth-set-default-theme -R flag documentation

💡 Result:

The -R flag is --rebuild-initrd. It tells plymouth-set-default-theme to rebuild the initial ramdisk (initrd) after changing the Plymouth theme (necessary because Plymouth is loaded from initrd). [1][2]

Example: sudo plymouth-set-default-theme -R THEME. [1]

Sources:
[1] Debian man page — plymouth-set-default-theme.
[2] Mankier / man page summary for plymouth-set-default-theme.


🌐 Web query:

what does plymouth-set-default-theme -R do restore default theme

💡 Result:

  • -R (or --rebuild-initrd) tells plymouth-set-default-theme to rebuild the initrd so the chosen theme is embedded into the initial ramdisk (necessary for the theme to actually appear at boot). [1][2]

  • It does not reset the theme to the default. Use -r (or --reset) to reset to the default theme; use -R after changing or resetting to ensure the initrd is rebuilt. [1][2]

Sources: [1] Debian manpage for plymouth-set-default-theme. [2] Arch/manpage (mankier) for plymouth-set-default-theme.


🌐 Web query:

plymouth-set-default-theme command syntax requires theme argument

💡 Result:

The script requires a theme name when you intend to change the theme. Usage (from the man page):

  • Show current theme: plymouth-set-default-theme
  • List available themes: plymouth-set-default-theme -l. [1]
  • Set a theme: sudo plymouth-set-default-theme THEME (optionally add -R or --rebuild-initrd to rebuild initrd). [1][2]

Note: On Ubuntu the recommended method is update-alternatives --config default.plymouth to choose the default.plymouth file. [2]

Sources:
[1] plymouth-set-default-theme man page. [2] Ask Ubuntu — how to install/choose Plymouth themes.


🌐 Web query:

what happens plymouth-set-default-theme -R without theme name error

💡 Result:

If you run plymouth-set-default-theme -R without giving a THEME, the command uses the currently selected theme and rebuilds the initrd (i.e. regenerates the initial ramdisk for that current theme). The manpage documents that invoking with no parameters shows the current theme, and -R/--rebuild-initrd rebuilds the initrd. [1][2]

Sources:

  • plymouth-set-default-theme man page (description and -R option). [1]
  • Arch/manpage copy (synopsis/behavior). [2]

Removal path doesn't reliably restore Plymouth theme and indiscriminately re-enables MOTD scripts.

  • plymouth-set-default-theme -R (Line 12) rebuilds the initrd for the currently set theme but does not restore the theme that existed before the package was installed. The package should either save the previous theme in postinst and restore it here, or use -r -R to reset to the system default and rebuild.
  • The loop chmod +x /etc/update-motd.d/??-* (Lines 17-19) re-enables all scripts indiscriminately, but postinst selectively disables non-cortex scripts (postinst lines 34–39). Without tracking which scripts were actually disabled by this package, prerm cannot safely restore the original state.
Recommended approach (requires pairing with postinst to persist state)
-        if command -v plymouth-set-default-theme &>/dev/null; then
-            plymouth-set-default-theme -R 2>/dev/null || true
-        fi
+        if command -v plymouth-set-default-theme &>/dev/null; then
+            if [ -f /var/lib/cortex-branding/plymouth-theme.prev ]; then
+                prev_theme="$(cat /var/lib/cortex-branding/plymouth-theme.prev)"
+                plymouth-set-default-theme "$prev_theme" -R 2>/dev/null || true
+                rm -f /var/lib/cortex-branding/plymouth-theme.prev
+            fi
+        fi
@@
-        if [ -d /etc/update-motd.d ]; then
-            for f in /etc/update-motd.d/??-* ; do
-                chmod +x "$f" 2>/dev/null || true
-            done
-        fi
+        # Re-enable only scripts we disabled (tracked in postinst)
+        if [ -f /var/lib/cortex-branding/motd-disabled.list ]; then
+            while IFS= read -r f; do
+                [ -e "$f" ] && chmod +x "$f" 2>/dev/null || true
+            done < /var/lib/cortex-branding/motd-disabled.list
+            rm -f /var/lib/cortex-branding/motd-disabled.list
+        fi
🤖 Prompt for AI Agents
In @packages/cortex-branding/DEBIAN/prerm around lines 6 - 21, The prerm
currently calls "plymouth-set-default-theme -R" which only rebuilds initrd for
the current theme; change the flow so the package either saves the prior theme
in postinst (e.g., write the original theme name to a persistent state file) and
then prerm reads that state and restores it, or replace "-R" with "-r -R" to
reset to system default and rebuild; likewise, stop indiscriminately chmod'ing
all /etc/update-motd.d/??-* scripts — have postinst record exactly which MOTD
scripts this package disabled (e.g., write their filenames to a state file) and
make prerm iterate only over that recorded list to re-enable (chmod +x) those
files, leaving other scripts untouched (refer to the postinst logic that
disables non-cortex scripts in lines 34–39 for exact criteria).

Comment on lines +1 to +20
Package: cortex-core
Version: 0.1.0
Section: metapackages
Priority: optional
Architecture: all
Depends: cortex-archive-keyring, cortex-upgrade, cortex-gpu, cortex-verify
Recommends: neofetch, htop, curl, wget, git
Suggests: cortex-full, cortex-secops
Maintainer: Cortex Linux Team <team@cortexlinux.com>
Homepage: https://cortexlinux.com
Description: Cortex Linux core meta-package with CLI tools
This meta-package installs the core Cortex Linux components:
.
- cortex-upgrade: Safe system upgrades with LVM/Btrfs/ZFS snapshot rollback
- cortex-gpu: GPU detection, NVIDIA/AMD driver enablement, Secure Boot MOK setup
- cortex-verify: Offline integrity verification
- cortex-archive-keyring: GPG keyring for Cortex APT repository trust
.
Install this package to get the essential Cortex Linux tools on any
Debian or Ubuntu system.
Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Potential issue | 🔴 Critical

🧩 Analysis chain

🏁 Script executed:

#!/bin/bash
# Search for the dependency packages in this repository
for pkg in cortex-archive-keyring cortex-upgrade cortex-gpu cortex-verify cortex-full cortex-secops; do
  echo "Searching for package: $pkg"
  fd -t d "^${pkg}$" packages/ || echo "  Not found in packages/"
done

Repository: cortexlinux/cortex-distro

Length of output: 374


🏁 Script executed:

#!/bin/bash
# Search more broadly for the missing packages
echo "=== Searching entire repository for package definitions ==="
for pkg in cortex-upgrade cortex-gpu cortex-verify; do
  echo ""
  echo "Looking for $pkg:"
  fd -t d "$pkg" . 2>/dev/null | head -20
done

echo ""
echo "=== Checking git ls-files for these package names ==="
git ls-files | grep -E "(cortex-upgrade|cortex-gpu|cortex-verify)" | head -20

Repository: cortexlinux/cortex-distro

Length of output: 756


🏁 Script executed:

#!/bin/bash
# Check if cortex-gpu-amd and cortex-gpu-nvidia have proper control files
echo "=== Checking GPU package control files ==="
for dir in packages/cortex-gpu-amd packages/cortex-gpu-nvidia; do
  if [ -f "$dir/DEBIAN/control" ]; then
    echo "Found: $dir/DEBIAN/control"
    sed -n '1,3p' "$dir/DEBIAN/control"
  elif [ -f "$dir/debian/control" ]; then
    echo "Found: $dir/debian/control"
    sed -n '1,3p' "$dir/debian/control"
  fi
done

echo ""
echo "=== Searching for cortex-upgrade and cortex-verify package definitions ==="
find packages -name "control" -type f -exec grep -l "Package: cortex-upgrade\|Package: cortex-verify" {} \;

echo ""
echo "=== List all packages with DEBIAN or debian control files ==="
find packages -name "control" -type f | sed 's|/[^/]*/control||' | sort -u

Repository: cortexlinux/cortex-distro

Length of output: 655


Fix critical dependency declaration on line 6.

The Depends field declares 4 packages, but only 1 exists in the repository:

  • cortex-archive-keyring — exists
  • cortex-upgrade — does not exist
  • cortex-gpu — does not exist (repository has cortex-gpu-amd and cortex-gpu-nvidia instead)
  • cortex-verify — does not exist

Missing or incorrectly named dependencies will cause installation failures. Update line 6 to declare only packages that exist, or replace cortex-gpu with the appropriate GPU variant(s) if this is a virtual package that should be provided elsewhere.

🤖 Prompt for AI Agents
In @packages/cortex-core/DEBIAN/control around lines 1 - 20, The control file's
Depends field lists non-existent packages causing install failures; open the
DEBIAN control and update the Depends line to reference only existing packages
(keep cortex-archive-keyring), remove or replace cortex-upgrade and
cortex-verify with their correct package names if they exist, and replace
cortex-gpu with the actual GPU package(s) (e.g., cortex-gpu-amd and/or
cortex-gpu-nvidia) or change it to a Suggests/Provides if it’s meant to be
virtual; ensure the Depends field remains a comma-separated list and adjust
Recommends/Suggests accordingly if you move packages out of Depends.

Copy link

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 4

🤖 Fix all issues with AI agents
In @Makefile:
- Around line 194-201: The Makefile rule currently copies preseed and
provisioning files using cp with failures suppressed (commands with 2>/dev/null
|| true), which hides missing files; update the rule to perform the copies
normally and then explicitly validate that expected files exist in
$(BUILD_DIR)/$(1)/config/includes.binary/preseed/,
$(BUILD_DIR)/$(1)/config/includes.binary/preseed/profiles/,
$(BUILD_DIR)/$(1)/config/includes.binary/preseed/partitioning/ and
$(BUILD_DIR)/$(1)/config/includes.binary/provisioning/ (use shell tests like [
-e ] or glob checks) and if any required files from $(PRESEED_DIR) or
$(PROVISION_DIR) are missing emit an error message and exit non‑zero so the
build fails early; keep references to the existing variables PRESEED_DIR,
PROVISION_DIR, BUILD_DIR and the copy commands to locate where to add the
checks.
- Around line 205-206: The mv invocation that moves the generated ISO currently
swallows errors via `2>/dev/null || true`, masking failures; remove the
redirection and the `|| true` and instead ensure the move reports failures and
fails the build (e.g., let mv return a non-zero exit code or on failure print a
clear error and exit 1). Update the line with the `mv
$(BUILD_DIR)/$(1)/live-image-$(ARCH).hybrid.iso
$(OUTPUT_DIR)/$(ISO_NAME)-$(1)-$(ISO_VERSION)-$(ARCH).iso` invocation so any
failure is propagated (or explicitly handled by printing an error mentioning the
ISO name and exiting non-zero) to prevent false-success builds.
- Around line 160-177: The Makefile silences copy failures with "2>/dev/null ||
true" causing missing branding/hook/bootloader assets to go unnoticed; update
the copy blocks referencing BUILD_DIR and ISO_DIR (the package-lists cp, hooks
cp, includes.chroot/includes.binary/bootloaders cp blocks) to validate source
existence before copying and let failures surface: remove the "2>/dev/null ||
true" for critical assets, replace with explicit existence checks (e.g. if [ -d
"$(ISO_DIR)/live-build/config/includes.chroot" ]; then cp -r ... || { echo >&2
"Failed to copy includes.chroot"; exit 1; }; else echo >&2 "Missing
includes.chroot"; exit 1; fi), and decide which noncritical assets may log a
warning instead of exiting so builds fail visibly when required branding or
bootloader files are absent.

In @scripts/build.sh:
- Around line 180-197: In scripts/build.sh, validate the required branding
assets before copying instead of silencing errors: check that all expected files
(e.g., "${PROJECT_ROOT}/branding/grub/cortex/theme.txt" and the specific PNGs
like background.png and any other required *.png) exist and log a clear error
then exit non‑zero if any required file is missing; replace the silent cp
"${PROJECT_ROOT}/branding/grub/cortex/"*.png 2>/dev/null || true with an
explicit existence check and selective copy of known filenames (or iterate the
glob after confirming it expands) so missing files cause a clear failure, and
avoid using a broad wildcard when copying to
config/includes.chroot/boot/grub/themes/cortex—use the validated list of files
to copy and use consistent logging (log error + exit) on validation failures
rather than mixing silent suppression and hard failures.
📜 Review details

Configuration used: defaults

Review profile: CHILL

Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between 4e631aa and 82ebb7a.

📒 Files selected for processing (8)
  • .gitignore
  • Makefile
  • iso/live-build/auto/build
  • iso/live-build/auto/clean
  • iso/live-build/auto/config
  • iso/live-build/config/bootloaders/grub-pc/config.cfg
  • iso/live-build/config/package-lists/cortex-core.list.chroot
  • scripts/build.sh
💤 Files with no reviewable changes (4)
  • iso/live-build/auto/clean
  • iso/live-build/auto/build
  • iso/live-build/config/package-lists/cortex-core.list.chroot
  • iso/live-build/auto/config
🚧 Files skipped from review as they are similar to previous changes (2)
  • iso/live-build/config/bootloaders/grub-pc/config.cfg
  • .gitignore
🧰 Additional context used
🪛 checkmake (0.2.2)
Makefile

[warning] 52-52: Target body for "help" exceeds allowed length of 5 (45).

(maxbodylength)


[warning] 136-136: Target body for "lint" exceeds allowed length of 5 (6).

(maxbodylength)


[warning] 241-241: Target body for "test" exceeds allowed length of 5 (14).

(maxbodylength)


[warning] 258-258: Target body for "clean" exceeds allowed length of 5 (7).

(maxbodylength)


[warning] 321-321: Target body for "config" exceeds allowed length of 5 (7).

(maxbodylength)


[warning] 320-320: Missing required phony target "clean"

(minphony)


[warning] 320-320: Missing required phony target "test"

(minphony)

⏰ Context from checks skipped due to timeout of 90000ms. You can increase the timeout in your CodeRabbit configuration to a maximum of 15 minutes (900000ms). (1)
  • GitHub Check: Build (First Pass)
🔇 Additional comments (1)
Makefile (1)

100-112: LGTM! Dependency checking is thorough and user-friendly.

The dependency validation logic properly checks for:

  • Required commands using command -v
  • live-build version compatibility with Debian Bookworm
  • Python 3.11+ with graceful degradation (warning vs error)

The implementation provides clear, actionable error messages to help users resolve dependency issues.

Your Name added 4 commits January 12, 2026 00:58
- Consolidated validation checks into a single build script.
- Updated dependency checks and validation functions for better error handling.
- Enhanced preseed and provisioning file management.
- Streamlined ISO build process with clearer output and logging.
- Improved branding package creation with better file handling and checks.
- Removed obsolete first-boot provisioning script and integrated its functionality into the build process.
- Added support for generating SBOM (Software Bill of Materials) during builds.
- Updated hooks and configuration scripts for better compatibility and error handling.
- Added support for multiple ISO profiles (core, full) in the build process.
- Introduced a validation job to check dependencies before building.
- Created a dedicated job for building the branding package.
- Updated workflow to include SBOM generation and improved artifact handling.
- Enhanced release process to include combined checksums and branding package downloads.
- Improved logging and output management for better traceability.
…treamline build process

- Added installation of git and sudo in the validation and build jobs.
- Consolidated dependency installation steps for improved clarity.
- Introduced a containerized environment using Debian Bookworm for all jobs.
- Removed redundant steps and improved overall workflow efficiency.
…lation

- Added 'make' to the list of packages installed in the build process.
- Improved clarity and completeness of the dependency management step.
Copy link

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 20

Note

Due to the large number of review comments, Critical, Major severity comments were prioritized as inline comments.

🤖 Fix all issues with AI agents
In @.github/workflows/build-iso.yml:
- Around line 44-60: The container jobs "validate" (runs 'make validate') and
the Branding Package job (runs 'make branding-package') install git/shellcheck
but not GNU make, causing the make invocations to fail; update the
package-install step(s) named "Install git" in the 'validate' job and the
corresponding install step in the Branding Package job to include make (e.g.,
add 'make' or 'build-essential' to the apt-get install -y list) so that the
subsequent 'make validate' and 'make branding-package' commands succeed.
- Around line 27-39: The workflow_dispatch input profile is unused and
misleading because jobs build-iso and test-iso currently run a fixed matrix
([core, full]) and ignore secops; update the jobs to honor the workflow_dispatch
input by filtering the matrix on manual runs (or by generating the matrix from
the input). Specifically, in the build-iso and test-iso jobs, reference
github.event.workflow_dispatch.inputs.profile and add an if condition to skip
matrix entries when the job was manually triggered (e.g., if: github.event_name
!= 'workflow_dispatch' ||
contains(split(github.event.workflow_dispatch.inputs.profile, ','),
matrix.profile)), or alternatively compute the matrix dynamically from the input
via fromJSON/values so secops can be included when provided; make sure to update
the matrix key name (matrix.profile) used by those jobs so selected profiles
actually run.
- Around line 19-27: The pull_request path filter currently omits the workflow
itself so PRs that only change .github/workflows/build-iso.yml won’t trigger CI;
update the pull_request: paths list (the pull_request.paths key in the workflow)
to include the workflow file (for example add '.github/workflows/build-iso.yml'
or a broader '.github/workflows/**' pattern), or remove the paths filter
entirely so workflow changes trigger the workflow; edit the build-iso.yml to
modify the pull_request.paths entry accordingly.
- Around line 235-236: Replace the GitHub Action reference to
softprops/action-gh-release@v1 with the v2 release to avoid Node 16 usage;
update the workflow step that currently uses softprops/action-gh-release@v1 to
softprops/action-gh-release@v2 (or a specific v2.x tag such as v2.5.0) so the
action runs on Node 20+.

In @iso/live-build/config/hooks/live/0100-cortex-config.hook.chroot:
- Around line 133-163: The unit can still run and fail if the first-boot script
is absent and calling systemctl enable in a chroot is unreliable; update the
unit (cortex-first-boot.service) to add a
ConditionPathExists=/opt/cortex/provisioning/first-boot.sh in addition to the
existing ConditionPathExists=!/opt/cortex/provisioning/.first-boot-complete so
the unit won’t be scheduled when the script is missing, and replace the runtime
systemctl enable call with creating the multi-user.target symlink yourself
(create a symlink from the cortex-first-boot.service unit into
multi-user.target.wants) so the unit is enabled in the offline/chroot build
environment without requiring a running systemd.

In @iso/live-build/config/hooks/live/50-cortex-branding.hook.chroot:
- Around line 9-12: The build hook currently uses non-deterministic network
fetches and only sets "set -e"; change the script to be failure-safe and
deterministic by adding "set -euo pipefail" at the top and replacing the remote
key download with a local, versioned key file shipped in the tree (e.g., use a
bundled file like cortex-apt-key.gpg and import it via apt-key add or gpg
--dearmor into /usr/share/keyrings), update the repository entry to reference
the local keyring, and ensure all commands that read from pipes will fail on
errors so failures are clear and reproducible.
- Around line 93-104: The GRUB_DISTRIBUTOR update uses `sed -i
's|^GRUB_DISTRIBUTOR=.*|GRUB_DISTRIBUTOR="Cortex Linux"|' /etc/default/grub ||
echo ...` which fails to append when no match is present because `sed` exits 0;
change the logic to explicitly test for existence (e.g., `if grep -q
"^GRUB_DISTRIBUTOR=" /etc/default/grub; then sed -i
's|^GRUB_DISTRIBUTOR=.*|GRUB_DISTRIBUTOR="Cortex Linux"|' /etc/default/grub;
else echo 'GRUB_DISTRIBUTOR="Cortex Linux"' >> /etc/default/grub; fi`) so that
`GRUB_DISTRIBUTOR` is replaced if present and appended if missing.
- Around line 49-63: This hook redundantly adds the apt.cortexlinux.com
repository and a different keyring (cortex-linux.gpg) which conflicts with the
canonical repo.cortexlinux.com/apt configured in 0100-cortex-config.hook.chroot
(which uses cortex-archive-keyring.gpg); remove the block that creates
/usr/share/keyrings/cortex-linux.gpg and the echo writing "deb
[signed-by=/usr/share/keyrings/cortex-linux.gpg] https://apt.cortexlinux.com
stable main" so only the canonical repo (repo.cortexlinux.com/apt with
cortex-archive-keyring.gpg) remains, or if consolidation is required replace the
apt.cortexlinux.com lines to match the repo.cortexlinux.com/apt entry and use
cortex-archive-keyring.gpg.

In @iso/live-build/config/hooks/live/99-configure-gnome.hook.chroot:
- Around line 8-44: The hook must skip GNOME/GDM steps when dconf isn't present
to avoid failing non-GNOME profiles; wrap the GNOME-specific sections (creation
of /etc/live/config.conf.d/cortex-gnome.conf, /etc/gdm3/daemon.conf and
/etc/gdm3/custom.conf, and the systemctl set-default graphical.target call) in a
conditional that checks for dconf (e.g. if command -v dconf >/dev/null 2>&1;
then ... fi), and replace the unconditional dconf update call with the same
guarded call pattern (mirroring the existing conditional used for
glib-compile-schemas).

In @iso/provisioning/first-boot.sh:
- Around line 41-58: check_already_complete calls log_info before logging is
initialized which can fail because LOG_DIR/LOG_FILE may not exist; move logging
initialization (create LOG_DIR and LOG_FILE) before any log_info calls or ensure
check_already_complete creates/validates LOG_DIR and LOG_FILE itself.
Specifically, either invoke init_logging() before calling check_already_complete
or add the same mkdir -p "${LOG_DIR}" && touch "${LOG_FILE}" && chmod 640
"${LOG_FILE}" steps into check_already_complete so log_info can safely write;
reference symbols: check_already_complete, init_logging, log_info, LOG_DIR,
LOG_FILE, STATE_FILE.
- Around line 471-484: The cleanup() function uses rm -f /var/log/installer/*
which can return non-zero if any entries are directories (and under set -e would
abort); change that to safely remove only files or tolerate directories: either
replace that line with a find invocation (e.g. use find /var/log/installer
-mindepth 1 -maxdepth 1 -type f -exec rm -f -- {} +) to delete only regular
files, or use rm -rf -- /var/log/installer/* || true to force recursive removal
and avoid exiting on non-zero. Ensure you update the line inside cleanup() where
rm -f /var/log/installer/* appears.
- Around line 97-131: Several places assume the cortex user exists and will
hard-fail; add an existence guard (e.g., check id -u cortex) before performing
user-specific actions in setup_password_change (guard the chage -d 0 cortex), in
full_desktop_setup (guard the groups cortex and usermod -aG docker cortex
calls), and in secops_hardening (guard writes to
/home/cortex/SECURITY-NOTICE.txt and chown cortex:cortex). If the check fails,
skip the user-specific steps and log a notice instead so the script remains
idempotent and tolerant of systems where the cortex user isn’t present.
- Around line 412-449: In setup_cortex_repository: ensure the keyring directory
exists before writing by running mkdir -p for the local variable keyring_dir,
stop piping curl directly into gpg and instead download the key to a temporary
file, verify its fingerprint against the expected value before importing,
dearmor and write the final key to ${keyring_dir}/cortex-linux.gpg, and set its
permissions to 0644 (chmod 644 "${keyring_dir}/cortex-linux.gpg"); keep using
the existing sources_file variable and existing apt-get update logic once the
key is validated and installed.
- Around line 132-184: In setup_ssh, the SSH hardening block is contradictory
and uses the wrong service name: the comment says "Disable password
authentication for root" while the file sets PasswordAuthentication yes, and the
reload checks for sshd service; change PasswordAuthentication to no (or update
the comment to accurately reflect that password auth remains enabled if
intended) so the config and comment align, and replace systemctl
is-active/reload checks referencing sshd with the Debian-standard service name
ssh (i.e., use systemctl is-active --quiet ssh and systemctl reload ssh ||
true); refer to the setup_ssh function and the sshd_config variable to locate
the changes.

In @repository/scripts/repo-manage.sh:
- Around line 136-143: The script currently uses touch "${arch_dir}/Packages"
which can leave stale content if the file already exists; change it to truncate
the file instead (overwrite with an empty file) when dpkg-scanpackages is not
run so Packages is emptied rather than preserved; update the branch in
repo-manage.sh that handles the else path after dpkg-scanpackages (the block
that currently calls touch "${arch_dir}/Packages") to create an empty/truncated
"${arch_dir}/Packages" before gzip -9 -k -f runs.

In @scripts/build.sh:
- Around line 94-154: The script currently treats dpkg-deb as optional in
cmd_check_deps but branding-package needs it; either mark dpkg-deb as required
by changing its check in cmd_check_deps to mirror other required checks (use
check_command dpkg-deb || failed=1 and ensure the final failed logic returns
non-zero), or add a dedicated runtime check inside cmd_branding_package that
tests command -v dpkg-deb and exits/fails with a clear error message if missing;
update references to the failed variable and error/warn calls so the failure
path is deterministic and the message explains that branding-package requires
dpkg-deb.
- Around line 67-76: The function copy_glob_if_exists has an unquoted glob and
uses cp without -r which breaks on filenames with spaces, directories, and
creates flaky builds; update it to safely expand globs into an array (or enable
nullglob), check if the array is non-empty, and iterate over matches quoting
each item when copying, and use cp -a (or cp -r --) to preserve directories and
handle recursive copies; keep references to the function name
copy_glob_if_exists and the variables pattern and dest when making these
changes.
- Around line 570-572: The live-build commands use unconditional sudo which
fails when running as root; update both invocations of "lb build" and "lb clean"
to run conditionally based on EUID: replace each `sudo lb build` and `sudo lb
clean` with an EUID check that runs `lb build`/`lb clean` directly if $EUID is
0, otherwise prefixes with sudo (e.g., if [[ $EUID -eq 0 ]]; then lb build; else
sudo lb build; fi and same pattern for lb clean).
🟡 Minor comments (4)
packages/cortex-branding/DEBIAN/prerm-15-20 (1)

15-20: Re-enabling every /etc/update-motd.d/??-* script can unintentionally override admin/system intent.

If you can’t restore prior chmod state, consider only re-enabling known distro defaults (or only undo what your postinst changed).

scripts/build.sh-600-610 (1)

600-610: SBOM generation should create the output dir (and consider not chmod +x-ing repo files during builds).

At least mkdir -p "${OUTPUT_DIR}/sbom" before invoking the generator.

iso/provisioning/first-boot.sh-361-389 (1)

361-389: Hardcoded FDE device path (/dev/sda3) is risky and profile-dependent.

This guidance appears only for the secops profile (when REQUIRE_FDE_PASSPHRASE_CHANGE=true), but assumes a fixed partition scheme. On systems installed to different disks (nvme0n1, /dev/sdb, etc.), the command will fail. Consider either:

  • Detecting the actual encrypted device at runtime (e.g., cryptsetup status cortex-sec-vg | grep device)
  • Injecting the device path via preseed/profile configuration
  • Documenting the assumption that installations must use /dev/sda
iso/live-build/config/hooks/live/50-cortex-branding.hook.chroot-124-147 (1)

124-147: Warn if GDM logo asset is missing (dconf will reference a non-existent file).

Line 135 hardcodes /usr/share/pixmaps/cortex-logo.png with no existence check (unlike wallpapers/plymouth/grub checks). Add a warning (or install it here) to avoid silent broken branding.

🧹 Nitpick comments (3)
iso/live-build/config/hooks/live/50-cortex-branding.hook.chroot (1)

263-266: Don’t let initramfs update break the ISO build unexpectedly.

Even if update-initramfs exists (Line 264), it can fail depending on kernel/initramfs state in the chroot. Consider making it non-fatal or gating on presence of kernels.

iso/live-build/config/hooks/live/0100-cortex-config.hook.chroot (2)

148-153: Hardening flags are explicitly disabled (verify this is intentional).

Lines 149-151 set NoNewPrivileges=false, ProtectSystem=false, ProtectHome=false. If the provisioning script truly needs broad writes, that’s fine, but then the “security hardening” intent is undermined.

If you want hardening here, consider NoNewPrivileges=true at minimum, and ProtectSystem=full + ReadWritePaths= for only the directories the script must mutate.


168-173: Service-existence check may be ineffective; confirm systemctl list-unit-files <unit> behavior.

Depending on systemd version, systemctl list-unit-files "$svc" can exit 0 even if the unit isn’t present, which defeats the guard.

📜 Review details

Configuration used: defaults

Review profile: CHILL

Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between 82ebb7a and aac8af0.

📒 Files selected for processing (12)
  • .github/workflows/build-iso.yml
  • Makefile
  • branding/install-branding.sh
  • iso/live-build/config/hooks/live/0100-cortex-config.hook.chroot
  • iso/live-build/config/hooks/live/50-cortex-branding.hook.chroot
  • iso/live-build/config/hooks/live/99-configure-gnome.hook.chroot
  • iso/live-build/config/includes.chroot/usr/lib/cortex/firstboot.sh
  • iso/provisioning/first-boot.sh
  • packages/cortex-branding/DEBIAN/postinst
  • packages/cortex-branding/DEBIAN/prerm
  • repository/scripts/repo-manage.sh
  • scripts/build.sh
💤 Files with no reviewable changes (1)
  • iso/live-build/config/includes.chroot/usr/lib/cortex/firstboot.sh
🚧 Files skipped from review as they are similar to previous changes (1)
  • branding/install-branding.sh
🧰 Additional context used
🪛 actionlint (1.7.10)
.github/workflows/build-iso.yml

236-236: the runner of "softprops/action-gh-release@v1" action is too old to run on GitHub Actions. update the action's version to fix this issue

(action)

🪛 checkmake (0.2.2)
Makefile

[warning] 46-46: Target body for "help" exceeds allowed length of 5 (44).

(maxbodylength)


[warning] 204-204: Target body for "config" exceeds allowed length of 5 (7).

(maxbodylength)


[warning] 16-16: Missing required phony target "clean"

(minphony)


[warning] 16-16: Missing required phony target "test"

(minphony)

⏰ Context from checks skipped due to timeout of 90000ms. You can increase the timeout in your CodeRabbit configuration to a maximum of 15 minutes (900000ms). (1)
  • GitHub Check: Build (First Pass)
🔇 Additional comments (3)
packages/cortex-branding/DEBIAN/prerm (1)

6-21: plymouth-set-default-theme -R without specifying a theme may not restore the “default” theme as intended.

If the goal is “undo cortex theme,” consider restoring the previously active theme (stored during postinst) or explicitly setting a known baseline theme for Debian Bookworm before running -R.

Makefile (1)

16-21: Makefile structure is clear and consistent with script-driven build.

(Optional) checkmake warnings about long help/config bodies are cosmetic and can be ignored unless you enforce that linter.

Also applies to: 46-91, 204-211

iso/live-build/config/hooks/live/99-configure-gnome.hook.chroot (1)

25-44: No action needed. The GDM autologin configuration and graphical.target change are already isolated to the live environment. The installed system is created fresh via debian-installer and preseed, which do not copy /etc/gdm3/ or other live-only configurations. If GNOME is installed on the target system (e.g., via cortex-full.preseed), it will be installed from packages with default GDM settings, not inheriting the live autologin configuration.

Likely an incorrect or invalid review comment.

Comment on lines 19 to 27
pull_request:
branches: [main]
paths:
- 'iso/**'
- 'branding/**'
- 'packages/**'
- 'scripts/**'
- 'Makefile'
workflow_dispatch:
Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Potential issue | 🟠 Major

PR path filter omits the workflow file, so CI won’t run when only the workflow changes.

Push includes .github/workflows/build-iso.yml (Line 18) but pull_request does not (Lines 21-27).

Proposed fix
   pull_request:
     branches: [main]
     paths:
       - 'iso/**'
       - 'branding/**'
       - 'packages/**'
       - 'scripts/**'
       - 'Makefile'
+      - '.github/workflows/build-iso.yml'
🤖 Prompt for AI Agents
In @.github/workflows/build-iso.yml around lines 19 - 27, The pull_request path
filter currently omits the workflow itself so PRs that only change
.github/workflows/build-iso.yml won’t trigger CI; update the pull_request: paths
list (the pull_request.paths key in the workflow) to include the workflow file
(for example add '.github/workflows/build-iso.yml' or a broader
'.github/workflows/**' pattern), or remove the paths filter entirely so workflow
changes trigger the workflow; edit the build-iso.yml to modify the
pull_request.paths entry accordingly.

Comment on lines +133 to 163
# Cortex first-boot service (canonical version from iso/provisioning/)
# Note: The actual first-boot.sh is deployed via preseed to /opt/cortex/provisioning/
cat > /etc/systemd/system/cortex-first-boot.service << 'EOF'
[Unit]
Description=Cortex Linux First Boot Provisioning
After=network-online.target
Wants=network-online.target
ConditionPathExists=!/etc/cortex/.firstboot-complete
Description=Cortex Linux First-Boot Provisioning
Documentation=https://github.com/cortexlinux/cortex-distro
After=network-pre.target systemd-resolved.service
Before=network.target getty@tty1.service display-manager.service
Wants=network-pre.target
ConditionPathExists=!/opt/cortex/provisioning/.first-boot-complete
[Service]
Type=oneshot
ExecStart=/usr/lib/cortex/firstboot.sh
ExecStartPost=/usr/bin/touch /etc/cortex/.firstboot-complete
ExecStart=/opt/cortex/provisioning/first-boot.sh
RemainAfterExit=yes
TimeoutSec=600
NoNewPrivileges=false
ProtectSystem=false
ProtectHome=false
PrivateTmp=true
StandardOutput=journal+console
StandardError=journal+console
SyslogIdentifier=cortex-first-boot
[Install]
WantedBy=multi-user.target
EOF

# Enable first-boot service
systemctl enable cortex-firstboot.service
systemctl enable cortex-first-boot.service

Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Potential issue | 🟠 Major

First-boot unit: ensure it won’t run (and fail) on live boot, and don’t rely on systemctl enable working in chroot.

  • The unit only checks !/opt/cortex/provisioning/.first-boot-complete (Line 142). If /opt/cortex/provisioning/first-boot.sh isn’t present in the live environment, it’ll still attempt to start and error.
  • systemctl enable ... in a build chroot (Line 162) is a common failure mode unless done in offline mode (or by creating the symlink yourself).
Proposed fix (safer conditions + enable without needing a running systemd)
 [Unit]
@@
-ConditionPathExists=!/opt/cortex/provisioning/.first-boot-complete
+ConditionPathExists=/opt/cortex/provisioning/first-boot.sh
+ConditionPathExists=!/opt/cortex/provisioning/.first-boot-complete
-# Enable first-boot service
-systemctl enable cortex-first-boot.service
+# Enable first-boot service (avoid requiring a running systemd in chroot)
+mkdir -p /etc/systemd/system/multi-user.target.wants
+ln -sf ../cortex-first-boot.service /etc/systemd/system/multi-user.target.wants/cortex-first-boot.service
📝 Committable suggestion

‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.

Suggested change
# Cortex first-boot service (canonical version from iso/provisioning/)
# Note: The actual first-boot.sh is deployed via preseed to /opt/cortex/provisioning/
cat > /etc/systemd/system/cortex-first-boot.service << 'EOF'
[Unit]
Description=Cortex Linux First Boot Provisioning
After=network-online.target
Wants=network-online.target
ConditionPathExists=!/etc/cortex/.firstboot-complete
Description=Cortex Linux First-Boot Provisioning
Documentation=https://github.com/cortexlinux/cortex-distro
After=network-pre.target systemd-resolved.service
Before=network.target getty@tty1.service display-manager.service
Wants=network-pre.target
ConditionPathExists=!/opt/cortex/provisioning/.first-boot-complete
[Service]
Type=oneshot
ExecStart=/usr/lib/cortex/firstboot.sh
ExecStartPost=/usr/bin/touch /etc/cortex/.firstboot-complete
ExecStart=/opt/cortex/provisioning/first-boot.sh
RemainAfterExit=yes
TimeoutSec=600
NoNewPrivileges=false
ProtectSystem=false
ProtectHome=false
PrivateTmp=true
StandardOutput=journal+console
StandardError=journal+console
SyslogIdentifier=cortex-first-boot
[Install]
WantedBy=multi-user.target
EOF
# Enable first-boot service
systemctl enable cortex-firstboot.service
systemctl enable cortex-first-boot.service
# Cortex first-boot service (canonical version from iso/provisioning/)
# Note: The actual first-boot.sh is deployed via preseed to /opt/cortex/provisioning/
cat > /etc/systemd/system/cortex-first-boot.service << 'EOF'
[Unit]
Description=Cortex Linux First-Boot Provisioning
Documentation=https://github.com/cortexlinux/cortex-distro
After=network-pre.target systemd-resolved.service
Before=network.target getty@tty1.service display-manager.service
Wants=network-pre.target
ConditionPathExists=/opt/cortex/provisioning/first-boot.sh
ConditionPathExists=!/opt/cortex/provisioning/.first-boot-complete
[Service]
Type=oneshot
ExecStart=/opt/cortex/provisioning/first-boot.sh
RemainAfterExit=yes
TimeoutSec=600
NoNewPrivileges=false
ProtectSystem=false
ProtectHome=false
PrivateTmp=true
StandardOutput=journal+console
StandardError=journal+console
SyslogIdentifier=cortex-first-boot
[Install]
WantedBy=multi-user.target
EOF
# Enable first-boot service (avoid requiring a running systemd in chroot)
mkdir -p /etc/systemd/system/multi-user.target.wants
ln -sf ../cortex-first-boot.service /etc/systemd/system/multi-user.target.wants/cortex-first-boot.service
🤖 Prompt for AI Agents
In @iso/live-build/config/hooks/live/0100-cortex-config.hook.chroot around lines
133 - 163, The unit can still run and fail if the first-boot script is absent
and calling systemctl enable in a chroot is unreliable; update the unit
(cortex-first-boot.service) to add a
ConditionPathExists=/opt/cortex/provisioning/first-boot.sh in addition to the
existing ConditionPathExists=!/opt/cortex/provisioning/.first-boot-complete so
the unit won’t be scheduled when the script is missing, and replace the runtime
systemctl enable call with creating the multi-user.target symlink yourself
(create a symlink from the cortex-first-boot.service unit into
multi-user.target.wants) so the unit is enabled in the offline/chroot build
environment without requiring a running systemd.

Comment on lines +9 to +12
set -e

echo "Installing Cortex Linux branding..."

Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🛠️ Refactor suggestion | 🟠 Major

Make the APT key/repo setup deterministic and failure-safe (avoid network fetch in build hook).

Line 57 downloads a signing key during the build; that’s brittle (network/endpoint changes) and a supply-chain risk. Also, with set -e (Line 9) and no pipefail, failures can be confusing in pipelines.

Proposed direction (ship key in tree + safer bash settings)
-set -e
+set -euo pipefail
-# Download and install GPG key
-mkdir -p /usr/share/keyrings
-curl -fsSL https://apt.cortexlinux.com/keys/cortex-linux.gpg.asc | gpg --dearmor -o /usr/share/keyrings/cortex-linux.gpg
+mkdir -p /usr/share/keyrings
+# Prefer: include the armored key in the repo and install it from includes.chroot (no network).
+# If you must download here, at least verify a pinned fingerprint after download.
+curl -fsSL "$CORTEX_KEY_URL" | gpg --dearmor -o /usr/share/keyrings/cortex-linux.gpg

Also applies to: 55-61

🤖 Prompt for AI Agents
In @iso/live-build/config/hooks/live/50-cortex-branding.hook.chroot around lines
9 - 12, The build hook currently uses non-deterministic network fetches and only
sets "set -e"; change the script to be failure-safe and deterministic by adding
"set -euo pipefail" at the top and replacing the remote key download with a
local, versioned key file shipped in the tree (e.g., use a bundled file like
cortex-apt-key.gpg and import it via apt-key add or gpg --dearmor into
/usr/share/keyrings), update the repository entry to reference the local
keyring, and ensure all commands that read from pipes will fail on errors so
failures are clear and reproducible.

Comment on lines 136 to 143
# Generate Packages file
log "Generating Packages for ${suite}/${arch}..."
(cd "${REPO_DIR}" && dpkg-scanpackages --arch "$arch" pool/main > "${arch_dir}/Packages" 2>/dev/null || true)
if [ -d "${REPO_DIR}/pool/main" ]; then
(cd "${REPO_DIR}" && dpkg-scanpackages --arch "$arch" pool/main > "${arch_dir}/Packages")
else
touch "${arch_dir}/Packages"
fi
gzip -9 -k -f "${arch_dir}/Packages"
Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Potential issue | 🟠 Major

touch Packages can leave stale repository metadata; truncate instead.

Proposed fix
-            if [ -d "${REPO_DIR}/pool/main" ]; then
-                (cd "${REPO_DIR}" && dpkg-scanpackages --arch "$arch" pool/main > "${arch_dir}/Packages")
-            else
-                touch "${arch_dir}/Packages"
-            fi
+            if [ -d "${REPO_DIR}/pool/main" ]; then
+                (cd "${REPO_DIR}" && dpkg-scanpackages --arch "$arch" pool/main > "${arch_dir}/Packages")
+            else
+                : > "${arch_dir}/Packages"
+            fi
📝 Committable suggestion

‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.

Suggested change
# Generate Packages file
log "Generating Packages for ${suite}/${arch}..."
(cd "${REPO_DIR}" && dpkg-scanpackages --arch "$arch" pool/main > "${arch_dir}/Packages" 2>/dev/null || true)
if [ -d "${REPO_DIR}/pool/main" ]; then
(cd "${REPO_DIR}" && dpkg-scanpackages --arch "$arch" pool/main > "${arch_dir}/Packages")
else
touch "${arch_dir}/Packages"
fi
gzip -9 -k -f "${arch_dir}/Packages"
# Generate Packages file
log "Generating Packages for ${suite}/${arch}..."
if [ -d "${REPO_DIR}/pool/main" ]; then
(cd "${REPO_DIR}" && dpkg-scanpackages --arch "$arch" pool/main > "${arch_dir}/Packages")
else
: > "${arch_dir}/Packages"
fi
gzip -9 -k -f "${arch_dir}/Packages"
🤖 Prompt for AI Agents
In @repository/scripts/repo-manage.sh around lines 136 - 143, The script
currently uses touch "${arch_dir}/Packages" which can leave stale content if the
file already exists; change it to truncate the file instead (overwrite with an
empty file) when dpkg-scanpackages is not run so Packages is emptied rather than
preserved; update the branch in repo-manage.sh that handles the else path after
dpkg-scanpackages (the block that currently calls touch "${arch_dir}/Packages")
to create an empty/truncated "${arch_dir}/Packages" before gzip -9 -k -f runs.

Comment on lines +67 to +76
copy_glob_if_exists() {
local pattern="$1"
local dest="$2"
# shellcheck disable=SC2086
if ls $pattern 1>/dev/null 2>&1; then
cp $pattern "$dest"
return 0
fi
return 1
}
Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Potential issue | 🔴 Critical

copy_glob_if_exists: unquoted glob + cp without -r will break on directories/spaces and can create flaky builds.

Proposed fix (safe globbing + recursive copy)
 copy_glob_if_exists() {
     local pattern="$1"
     local dest="$2"
-    # shellcheck disable=SC2086
-    if ls $pattern 1>/dev/null 2>&1; then
-        cp $pattern "$dest"
-        return 0
-    fi
-    return 1
+    shopt -s nullglob
+    local matches=($pattern)
+    shopt -u nullglob
+    if ((${#matches[@]} > 0)); then
+        cp -r -- "${matches[@]}" "$dest"
+        return 0
+    fi
+    return 1
 }
📝 Committable suggestion

‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.

Suggested change
copy_glob_if_exists() {
local pattern="$1"
local dest="$2"
# shellcheck disable=SC2086
if ls $pattern 1>/dev/null 2>&1; then
cp $pattern "$dest"
return 0
fi
return 1
}
copy_glob_if_exists() {
local pattern="$1"
local dest="$2"
shopt -s nullglob
local matches=($pattern)
shopt -u nullglob
if ((${#matches[@]} > 0)); then
cp -r -- "${matches[@]}" "$dest"
return 0
fi
return 1
}
🤖 Prompt for AI Agents
In @scripts/build.sh around lines 67 - 76, The function copy_glob_if_exists has
an unquoted glob and uses cp without -r which breaks on filenames with spaces,
directories, and creates flaky builds; update it to safely expand globs into an
array (or enable nullglob), check if the array is non-empty, and iterate over
matches quoting each item when copying, and use cp -a (or cp -r --) to preserve
directories and handle recursive copies; keep references to the function name
copy_glob_if_exists and the variables pattern and dest when making these
changes.

Comment on lines +94 to +154
cmd_check_deps() {
local failed=0

header "Checking build dependencies"

# Required dependencies
check_command lb live-build || failed=1
check_command gpg gnupg || failed=1
check_command python3 python3 || failed=1

# Check live-build version
if command -v lb &>/dev/null; then
local lb_version
lb_version=$(dpkg-query -W -f='${Version}' live-build 2>/dev/null || echo "0")
if dpkg --compare-versions "$lb_version" lt "1:20210814"; then
warn "live-build version $lb_version may be too old. Recommended: >= 1:20210814"
else
log "live-build version: $lb_version"
fi
fi

# Check Python version
if command -v python3 &>/dev/null; then
if python3 -c "import sys; sys.exit(0 if sys.version_info >= (3, 11) else 1)" 2>/dev/null; then
log "Python version: $(python3 --version 2>&1 | cut -d' ' -f2)"
else
warn "Python 3.11+ recommended (found: $(python3 --version 2>&1 | cut -d' ' -f2))"
fi
fi

# Optional dependencies
echo ""
log "Checking optional dependencies..."

if command -v shellcheck &>/dev/null; then
log "shellcheck: OK"
else
warn "Non-Debian OS - build may not work correctly"
fi

# Check for required commands
local required_cmds=(
"dpkg-buildpackage"
"lb"
"debootstrap"
"xorriso"
"mksquashfs"
)

for cmd in "${required_cmds[@]}"; do
if command -v "$cmd" &>/dev/null; then
log "Found: $cmd"
warn "shellcheck not installed (optional, for linting)"
fi

if command -v convert &>/dev/null; then
log "ImageMagick: OK"
else
warn "ImageMagick not installed (optional, for GRUB theme images)"
fi

if command -v dpkg-deb &>/dev/null; then
log "dpkg-deb: OK"
else
warn "dpkg-deb not installed (needed for branding-package)"
fi

echo ""
if [ $failed -eq 0 ]; then
log "All required dependencies found."
return 0
else
error "Missing required dependencies."
return 1
fi
}
Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Potential issue | 🟠 Major

branding-package needs dpkg-deb but dependency check treats it as optional.

Either make dpkg-deb required in cmd_check_deps (or check it inside cmd_branding_package and fail with a clear message).

Also applies to: 730-818

🤖 Prompt for AI Agents
In @scripts/build.sh around lines 94 - 154, The script currently treats dpkg-deb
as optional in cmd_check_deps but branding-package needs it; either mark
dpkg-deb as required by changing its check in cmd_check_deps to mirror other
required checks (use check_command dpkg-deb || failed=1 and ensure the final
failed logic returns non-zero), or add a dedicated runtime check inside
cmd_branding_package that tests command -v dpkg-deb and exits/fails with a clear
error message if missing; update references to the failed variable and
error/warn calls so the failure path is deterministic and the message explains
that branding-package requires dpkg-deb.

Comment on lines 554 to 560
# Copy provisioning files
if [ -d "$PROVISION_DIR" ]; then
if copy_glob_if_exists "${PROVISION_DIR}/*" "${build_path}/config/includes.binary/provisioning/"; then
log "Copied provisioning files"
fi
fi
}
Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Potential issue | 🟠 Major

Provisioning copy will inherit the copy_glob_if_exists problems (dirs inside provisioning will fail).

Fixing copy_glob_if_exists as above should address this too.

Comment on lines +570 to +572
log "Starting live-build (this may take a while)..."
sudo lb build

Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Potential issue | 🟠 Major

🧩 Analysis chain

🏁 Script executed:

# First, find the build.sh file
fd -t f "build.sh" scripts/

Repository: cortexlinux/cortex-distro

Length of output: 85


🏁 Script executed:

# Check if the file exists and get line count
wc -l scripts/build.sh 2>/dev/null || echo "File not found"

Repository: cortexlinux/cortex-distro

Length of output: 89


🏁 Script executed:

# Read the context around lines 570-572
if [ -f scripts/build.sh ]; then
  sed -n '560,580p' scripts/build.sh
fi

Repository: cortexlinux/cortex-distro

Length of output: 549


🏁 Script executed:

# Search for all instances of 'lb build' and 'lb clean' in the script
if [ -f scripts/build.sh ]; then
  echo "=== Instances of 'lb build' ===" && grep -n "lb build" scripts/build.sh
  echo "=== Instances of 'lb clean' ===" && grep -n "lb clean" scripts/build.sh
fi

Repository: cortexlinux/cortex-distro

Length of output: 180


🏁 Script executed:

# Read the beginning of the file to see shebang and initial documentation
head -50 scripts/build.sh

Repository: cortexlinux/cortex-distro

Length of output: 1529


🏁 Script executed:

# Check for existing EUID checks or sudo patterns in the file
grep -n "EUID\|getuid\|whoami\|id -u" scripts/build.sh || echo "No explicit user checks found"

Repository: cortexlinux/cortex-distro

Length of output: 98


🏁 Script executed:

# Look for other sudo commands to understand the pattern
grep -n "^[[:space:]]*sudo " scripts/build.sh | head -20

Repository: cortexlinux/cortex-distro

Length of output: 116


🏁 Script executed:

# Check surrounding context of the build_iso function
sed -n '550,600p' scripts/build.sh

Repository: cortexlinux/cortex-distro

Length of output: 1419


Unconditional sudo will fail when running as root (common in CI/CD environments); use conditional sudo for both lb build and lb clean.

Apply the EUID check to both occurrences:

  • Line 571: sudo lb build
  • Line 643: sudo lb clean

Use: if [[ $EUID -eq 0 ]]; then lb build; else sudo lb build; fi (and same pattern for lb clean).

🤖 Prompt for AI Agents
In @scripts/build.sh around lines 570 - 572, The live-build commands use
unconditional sudo which fails when running as root; update both invocations of
"lb build" and "lb clean" to run conditionally based on EUID: replace each `sudo
lb build` and `sudo lb clean` with an EUID check that runs `lb build`/`lb clean`
directly if $EUID is 0, otherwise prefixes with sudo (e.g., if [[ $EUID -eq 0
]]; then lb build; else sudo lb build; fi and same pattern for lb clean).

Copy link

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 2

🤖 Fix all issues with AI agents
In @.github/workflows/build-iso.yml:
- Line 236: The workflow currently pins the GitHub Action as
"softprops/action-gh-release@v1" which is outdated; update the uses entry for
softprops/action-gh-release to a newer release (e.g., "@v2" or a specific commit
SHA) to ensure compatibility with current runners and satisfy actionlint; locate
the "uses: softprops/action-gh-release@v1" line in the workflow and replace the
version tag with the chosen newer tag or SHA, then run the workflow lint/CI to
verify the change.
- Around line 28-38: The workflow defines an unused inputs.profile with options
[core, full, secops, all] while the build-iso and test-iso strategy matrices
(matrix in the build-iso job and matrix in the test-iso job) are hardcoded to
[core, full]; either remove the extra options from inputs.profile or make the
matrices respect the input by replacing their static arrays with the dynamic
value (use the inputs.profile value in the matrix expressions for both the
build-iso and test-iso jobs) so selecting secops or all actually affects which
profiles are built and tested.
🧹 Nitpick comments (3)
.github/workflows/build-iso.yml (3)

152-158: Re-evaluate continue-on-error for SBOM uploads.

Setting continue-on-error: true for SBOM uploads (software bill of materials) may hide issues with SBOM generation. While SBOMs might be optional for the build to succeed, their absence could have compliance or security implications.

Consider one of the following approaches:

  1. Remove continue-on-error if SBOMs are required for releases
  2. Add conditional logic to fail on tags but allow errors on PR builds
  3. Add explicit logging when SBOM generation fails

Example for conditional strictness:

      - name: Upload SBOM
        uses: actions/upload-artifact@v4
        with:
          name: sbom-${{ matrix.profile }}-${{ matrix.arch }}
          path: output/sbom/
          retention-days: 14
        continue-on-error: ${{ github.ref_type != 'tag' }}

182-198: Add explicit verification that ISO files exist.

The shell loops using *.iso will fail gracefully if no ISOs exist (thanks to the conditional check on line 186), but could be more explicit about the failure case.

Consider adding an explicit check:

      - name: Verify checksums
        run: |
          shopt -s nullglob
          isos=(*.iso)
          if [ ${#isos[@]} -eq 0 ]; then
            echo "Error: No ISO files found"
            exit 1
          fi
          for iso in "${isos[@]}"; do
            if [ -f "${iso}.sha256" ]; then
              sha256sum -c "${iso}.sha256"
            fi
          done

This makes the workflow fail more explicitly if artifacts are missing.


229-233: Add file existence checks before generating checksums.

The checksum generation assumes *.iso and *.deb files exist. If artifact downloads fail or produce no files, the sha256sum command will fail with a cryptic error.

Add verification before checksum generation:

      - name: Generate combined checksums
        run: |
          cd release
          # Verify files exist
          if ! ls *.iso >/dev/null 2>&1; then
            echo "Error: No ISO files found for release"
            exit 1
          fi
          if ! ls *.deb >/dev/null 2>&1; then
            echo "Error: No DEB packages found for release"
            exit 1
          fi
          sha256sum *.iso *.deb > SHA256SUMS
          sha512sum *.iso *.deb > SHA512SUMS
📜 Review details

Configuration used: defaults

Review profile: CHILL

Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between aac8af0 and 2882c18.

📒 Files selected for processing (1)
  • .github/workflows/build-iso.yml
🧰 Additional context used
🪛 actionlint (1.7.10)
.github/workflows/build-iso.yml

236-236: the runner of "softprops/action-gh-release@v1" action is too old to run on GitHub Actions. update the action's version to fix this issue

(action)

⏰ Context from checks skipped due to timeout of 90000ms. You can increase the timeout in your CodeRabbit configuration to a maximum of 15 minutes (900000ms). (3)
  • GitHub Check: Build ISO (core)
  • GitHub Check: Build ISO (full)
  • GitHub Check: Build (First Pass)
🔇 Additional comments (3)
.github/workflows/build-iso.yml (3)

44-59: LGTM!

The validation job correctly sets up dependencies and runs validation checks in a containerized environment.


76-84: Verify artifact path pattern matches expected output.

The wildcard output/*.deb assumes the branding package build produces at least one .deb file. If the build succeeds but no package is generated, the upload step will fail silently.

Consider adding a verification step before upload:

      - name: Verify package output
        run: |
          if [ ! -f output/*.deb ]; then
            echo "Error: No branding package found in output/"
            exit 1
          fi
          ls -lh output/*.deb

89-92: Document the security implications of --privileged.

The --privileged flag grants extended privileges to the container, which is necessary for live-build to access loop devices and create bootable ISOs. While this is acceptable in a controlled CI environment, it does expand the attack surface.

Ensure that:

  1. The repository has appropriate access controls
  2. Workflow permissions follow the principle of least privilege
  3. This requirement is documented in the build documentation

Consider adding a comment explaining why --privileged is necessary:

    container:
      image: debian:bookworm
      # Privileged mode required for live-build to access loop devices
      options: --privileged

sha512sum *.iso *.deb > SHA512SUMS
- name: Create Release
uses: softprops/action-gh-release@v1
Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Potential issue | 🟡 Minor

Update to the latest version of softprops/action-gh-release.

The v1 tag uses an outdated runner version that may not work on GitHub Actions. Update to a more recent version (e.g., v2 or a specific commit SHA).

Based on the static analysis hint from actionlint.

📦 Proposed fix
       - name: Create Release
-        uses: softprops/action-gh-release@v1
+        uses: softprops/action-gh-release@v2
         with:
📝 Committable suggestion

‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.

Suggested change
uses: softprops/action-gh-release@v1
uses: softprops/action-gh-release@v2
🧰 Tools
🪛 actionlint (1.7.10)

236-236: the runner of "softprops/action-gh-release@v1" action is too old to run on GitHub Actions. update the action's version to fix this issue

(action)

🤖 Prompt for AI Agents
In @.github/workflows/build-iso.yml at line 236, The workflow currently pins the
GitHub Action as "softprops/action-gh-release@v1" which is outdated; update the
uses entry for softprops/action-gh-release to a newer release (e.g., "@v2" or a
specific commit SHA) to ensure compatibility with current runners and satisfy
actionlint; locate the "uses: softprops/action-gh-release@v1" line in the
workflow and replace the version tag with the chosen newer tag or SHA, then run
the workflow lint/CI to verify the change.

Your Name added 3 commits January 12, 2026 01:54
…availability. Added a message to indicate the repository is currently skipped.
- Adjusted boot menu dimensions and item font sizes for better readability.
- Enhanced title and subtitle labels with updated text and styling.
- Refined help text positioning and font size for clarity.
- Added new message and background colors for improved visual appeal.
- Removed the container specification for the build job, simplifying the workflow.
- Added a disk cleanup step to free up space before the build process.
- Consolidated dependency installation into a Docker run command for better isolation and management.
- Ensured that the build process includes dependency checks and ISO creation in a single step.
Copy link

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 4

🤖 Fix all issues with AI agents
In @.github/workflows/build-iso.yml:
- Around line 27-38: The workflow_dispatch input "profile" is misleading because
the build matrix doesn't use it; update the workflow to either (A) drive the
build matrix from the "profile" input by mapping the input value to the
matrix.include or matrix.strategy (e.g., read github.event.inputs.profile in the
job and set matrix values to run core/full/secops/all accordingly), or (B)
restrict the workflow_dispatch options to only "core" and "full" to match the
current matrix; locate the workflow_dispatch block and the job that defines the
build matrix and change the matrix definition to conditionally use
github.event.inputs.profile (or add a mapping step to populate matrix.include)
or prune the input options to match the existing matrix.
- Around line 245-250: The "Generate combined checksums" step can fail when glob
patterns match nothing; change the run block to avoid bare globs by enabling
nullglob (shopt -s nullglob) or by building an explicit file list (e.g.,
files=(release/*.{iso,deb}) or use find) and only run sha256sum/sha512sum when
the list is non-empty; update the commands that call sha256sum and sha512sum to
use the guarded file array/variable so the step exits successfully when there
are no matches.
- Around line 193-215: The workflow's "Download ISO", "Verify checksums", and
"Check ISO structure" steps assume .iso files in the runner root but
actions/download-artifact extracts into a subfolder, causing the for-loops to
see a literal "*.iso" and skip checks; change the download step to save
artifacts into a known directory (e.g., an explicit target dir name) and then in
the "Verify checksums" and "Check ISO structure" steps cd into that directory
(or reference that directory in the loops) and enable safe globbing (e.g.,
nullglob) or use a secure find pattern so the verification and xorriso checks
actually iterate real .iso files.
- Around line 251-252: Update the GitHub Actions step that currently uses
softprops/action-gh-release@v1 to the maintained v2 release; locate the workflow
step with the uses: softprops/action-gh-release@v1 entry in the Create Release
job and change the reference to softprops/action-gh-release@v2 so the action
runs on Node 20-compatible version.
🧹 Nitpick comments (3)
.github/workflows/build-iso.yml (3)

44-60: Containerized validate job is good; drop sudo or set strict shell flags (Line 44–60).
In a Debian container you’re typically root already; sudo is extra surface area. Consider set -euo pipefail in the install step for cleaner failures.


96-151: Re-check need for docker run --privileged + add timeouts for apt (Line 96–151).
--privileged is a big hammer; if live-build only needs loop devices/mounts, consider narrower caps/devices. Also, apt-get with no retries/timeouts can hang a runner.


168-175: SBOM upload: consider if-no-files-found: ignore instead of continue-on-error (Line 168–175).
This avoids masking other upload errors while still allowing “no SBOM produced” to be non-fatal.

📜 Review details

Configuration used: defaults

Review profile: CHILL

Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between 922ee06 and e8211b8.

📒 Files selected for processing (2)
  • .github/workflows/build-iso.yml
  • branding/grub/cortex/theme.txt
🚧 Files skipped from review as they are similar to previous changes (1)
  • branding/grub/cortex/theme.txt
🧰 Additional context used
🪛 actionlint (1.7.10)
.github/workflows/build-iso.yml

252-252: the runner of "softprops/action-gh-release@v1" action is too old to run on GitHub Actions. update the action's version to fix this issue

(action)

⏰ Context from checks skipped due to timeout of 90000ms. You can increase the timeout in your CodeRabbit configuration to a maximum of 15 minutes (900000ms). (2)
  • GitHub Check: Build Branding Package
  • GitHub Check: Build (First Pass)
🔇 Additional comments (2)
.github/workflows/build-iso.yml (2)

12-26: Workflow triggers look fine, but ensure new paths are intentional (Line 12–26).
Adding branding/** and scripts/** will increase CI frequency; if that’s expected, LGTM.


61-85: Branding package build/upload looks consistent (Line 61–85).
Artifact name + output/*.deb convention is clear.

Comment on lines 193 to 215
- name: Download ISO
uses: actions/download-artifact@v4
with:
name: cortex-linux-${{ matrix.profile }}-${{ matrix.arch }}

- name: Verify checksums
run: |
ls -la
for iso in *.iso; do
if [ -f "${iso}.sha256" ]; then
sha256sum -c "${iso}.sha256"
fi
done
- name: Check ISO structure
run: |
sudo apt-get update
sudo apt-get install -y xorriso
for iso in *.iso; do
echo "=== Checking $iso ==="
xorriso -indev "$iso" -find / -maxdepth 1 2>/dev/null | head -20
done
Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Potential issue | 🔴 Critical

test-iso likely doesn’t test anything due to artifact extraction layout (Line 193–215).
actions/download-artifact will usually extract into a subfolder (artifact name) unless you flatten; your for iso in *.iso loops can end up iterating the literal *.iso and silently skipping verification.

Proposed fix: download into a known dir and `cd` there (also make globbing safe)
   test-iso:
@@
     steps:
       - name: Download ISO
         uses: actions/download-artifact@v4
         with:
           name: cortex-linux-${{ matrix.profile }}-${{ matrix.arch }}
+          path: artifacts

       - name: Verify checksums
         run: |
-          ls -la
-          for iso in *.iso; do
+          set -euo pipefail
+          shopt -s nullglob
+          cd artifacts
+          # If download-artifact created a subdir, enter it
+          d="cortex-linux-${{ matrix.profile }}-${{ matrix.arch }}"
+          [ -d "$d" ] && cd "$d"
+          ls -la
+          for iso in *.iso; do
             if [ -f "${iso}.sha256" ]; then
               sha256sum -c "${iso}.sha256"
             fi
           done

       - name: Check ISO structure
         run: |
+          set -euo pipefail
+          shopt -s nullglob
           sudo apt-get update
           sudo apt-get install -y xorriso
-          for iso in *.iso; do
+          cd artifacts
+          d="cortex-linux-${{ matrix.profile }}-${{ matrix.arch }}"
+          [ -d "$d" ] && cd "$d"
+          for iso in *.iso; do
             echo "=== Checking $iso ==="
             xorriso -indev "$iso" -find / -maxdepth 1 2>/dev/null | head -20
           done
🤖 Prompt for AI Agents
In @.github/workflows/build-iso.yml around lines 193 - 215, The workflow's
"Download ISO", "Verify checksums", and "Check ISO structure" steps assume .iso
files in the runner root but actions/download-artifact extracts into a
subfolder, causing the for-loops to see a literal "*.iso" and skip checks;
change the download step to save artifacts into a known directory (e.g., an
explicit target dir name) and then in the "Verify checksums" and "Check ISO
structure" steps cd into that directory (or reference that directory in the
loops) and enable safe globbing (e.g., nullglob) or use a secure find pattern so
the verification and xorriso checks actually iterate real .iso files.

Comment on lines +245 to 250
- name: Generate combined checksums
run: |
cd release
sha256sum *.iso *.deb > SHA256SUMS
sha512sum *.iso *.deb > SHA512SUMS
Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Potential issue | 🟡 Minor

Release checksum generation can fail on empty globs (Line 245–250).
If anything changes in naming/paths, sha256sum *.iso *.deb will error. Consider nullglob + explicit checks (or checksum everything under release/).

🤖 Prompt for AI Agents
In @.github/workflows/build-iso.yml around lines 245 - 250, The "Generate
combined checksums" step can fail when glob patterns match nothing; change the
run block to avoid bare globs by enabling nullglob (shopt -s nullglob) or by
building an explicit file list (e.g., files=(release/*.{iso,deb}) or use find)
and only run sha256sum/sha512sum when the list is non-empty; update the commands
that call sha256sum and sha512sum to use the guarded file array/variable so the
step exits successfully when there are no matches.

- Introduced a new action to free up disk space before the build process.
- Added additional cleanup commands to remove various unnecessary files and directories.
- Improved overall efficiency of the workflow by ensuring more space is available for builds.
Copy link

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 3

🤖 Fix all issues with AI agents
In @.github/workflows/build-iso.yml:
- Around line 29-38: The workflow_dispatch input declares profile options
'secops' and 'all' that the build matrix doesn't support; either remove those
unsupported options from the profile input (leave options as 'core' and 'full'
only) or modify the strategy.matrix.profile logic to evaluate github.event_name
and github.event.inputs.profile so a manual dispatch uses the input (supporting
'all' to expand to multiple profiles or a single-profile array) as shown in the
suggested conditional matrix expression; update both the input block named
profile and the strategy.matrix.profile handling so they stay consistent.
- Around line 259-260: The workflow's "Create Release" step uses the outdated
action reference softprops/action-gh-release@v1 which may fail on modern
runners; update the uses entry to the newer major version (e.g.,
softprops/action-gh-release@v2) in the Create Release job so the workflow pulls
the updated action release instead of @v1.
- Around line 96-105: The workflow step named "Free disk space" currently
references the external action jlumbroso/free-disk-space@main; change that
reference to a fixed, published version (e.g., jlumbroso/free-disk-space@v1.3.1)
to avoid supply-chain risks, updating the uses field in that step while leaving
the existing with: inputs unchanged.
🧹 Nitpick comments (1)
.github/workflows/build-iso.yml (1)

206-222: Consider more thorough ISO structure validation.

The current test only lists top-level contents. Consider verifying essential files exist (e.g., boot loader, squashfs, etc.) to catch structural issues:

💡 Enhanced structure verification
      - name: Check ISO structure
        run: |
          sudo apt-get update
          sudo apt-get install -y xorriso
          for iso in *.iso; do
            echo "=== Checking $iso ==="
            xorriso -indev "$iso" -find / -maxdepth 1 2>/dev/null | head -20
+           # Verify essential boot components exist
+           xorriso -indev "$iso" -find / -name "*.squashfs" 2>/dev/null | grep -q squashfs || echo "WARNING: No squashfs found"
+           xorriso -indev "$iso" -find /boot -maxdepth 2 2>/dev/null | head -10
          done
📜 Review details

Configuration used: defaults

Review profile: CHILL

Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between e8211b8 and b071385.

📒 Files selected for processing (1)
  • .github/workflows/build-iso.yml
🧰 Additional context used
🪛 actionlint (1.7.10)
.github/workflows/build-iso.yml

260-260: the runner of "softprops/action-gh-release@v1" action is too old to run on GitHub Actions. update the action's version to fix this issue

(action)

⏰ Context from checks skipped due to timeout of 90000ms. You can increase the timeout in your CodeRabbit configuration to a maximum of 15 minutes (900000ms). (3)
  • GitHub Check: Build ISO (core)
  • GitHub Check: Build ISO (full)
  • GitHub Check: Build (First Pass)
🔇 Additional comments (4)
.github/workflows/build-iso.yml (4)

44-59: LGTM!

The validate job is well-structured with appropriate dependencies installed in a consistent Debian container environment.


61-84: LGTM!

The branding package build job properly chains after validation and uploads the artifact for downstream jobs.


130-159: LGTM!

The containerized build approach with --privileged is appropriate for live-build operations that require loop device access and chroot capabilities. The set -e ensures the build fails fast on errors.


253-267: LGTM with a defensive improvement suggestion.

The checksum generation and release assets are well-organized. The branding package should always be present from the earlier job, so the glob pattern should match.

For robustness, you could add shopt -s nullglob to avoid errors if a glob pattern unexpectedly matches nothing:

💡 Optional: Defensive glob handling
      - name: Generate combined checksums
        run: |
          cd release
+          shopt -s nullglob
+          files=(*.iso *.deb)
+          if [ ${#files[@]} -eq 0 ]; then
+            echo "ERROR: No ISO or deb files found"
+            exit 1
+          fi
          sha256sum *.iso *.deb > SHA256SUMS
          sha512sum *.iso *.deb > SHA512SUMS

Comment on lines +96 to +105
- name: Free disk space
uses: jlumbroso/free-disk-space@main
with:
tool-cache: true
android: true
dotnet: true
haskell: true
large-packages: true
docker-images: true
swap-storage: true
Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Potential issue | 🟠 Major

🧩 Analysis chain

🌐 Web query:

jlumbroso/free-disk-space GitHub Action latest release version

💡 Result:

Latest release: v1.3.1 — released Oct 18, 2023. [1]

Sources
[1] jlumbroso/free-disk-space tags (v1.3.1).


Pin jlumbroso/free-disk-space to a stable version instead of @main.

Using @main exposes the build to supply chain risks—breaking changes or malicious commits could affect builds without notice. Pin to a stable release like v1.3.1:

Recommended change
      - name: Free disk space
-        uses: jlumbroso/free-disk-space@main
+        uses: jlumbroso/free-disk-space@v1.3.1
📝 Committable suggestion

‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.

Suggested change
- name: Free disk space
uses: jlumbroso/free-disk-space@main
with:
tool-cache: true
android: true
dotnet: true
haskell: true
large-packages: true
docker-images: true
swap-storage: true
- name: Free disk space
uses: jlumbroso/free-disk-space@v1.3.1
with:
tool-cache: true
android: true
dotnet: true
haskell: true
large-packages: true
docker-images: true
swap-storage: true
🤖 Prompt for AI Agents
In @.github/workflows/build-iso.yml around lines 96 - 105, The workflow step
named "Free disk space" currently references the external action
jlumbroso/free-disk-space@main; change that reference to a fixed, published
version (e.g., jlumbroso/free-disk-space@v1.3.1) to avoid supply-chain risks,
updating the uses field in that step while leaving the existing with: inputs
unchanged.

Comment on lines 259 to 260
- name: Create Release
uses: softprops/action-gh-release@v1
Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Potential issue | 🟡 Minor

Update softprops/action-gh-release to a newer version.

The static analysis tool indicates that @v1 is too old to run on current GitHub Actions runners. This could cause failures or deprecated behavior warnings.

🔧 Update to v2
      - name: Create Release
-        uses: softprops/action-gh-release@v1
+        uses: softprops/action-gh-release@v2
        with:
          files: |
📝 Committable suggestion

‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.

Suggested change
- name: Create Release
uses: softprops/action-gh-release@v1
- name: Create Release
uses: softprops/action-gh-release@v2
🧰 Tools
🪛 actionlint (1.7.10)

260-260: the runner of "softprops/action-gh-release@v1" action is too old to run on GitHub Actions. update the action's version to fix this issue

(action)

🤖 Prompt for AI Agents
In @.github/workflows/build-iso.yml around lines 259 - 260, The workflow's
"Create Release" step uses the outdated action reference
softprops/action-gh-release@v1 which may fail on modern runners; update the uses
entry to the newer major version (e.g., softprops/action-gh-release@v2) in the
Create Release job so the workflow pulls the updated action release instead of
@v1.

…ld process

- Simplified ISO build targets in the Makefile, consolidating profiles into a single command.
- Updated help text to reflect changes in ISO build commands and removed obsolete profiles.
- Enhanced GitHub Actions workflow to support both amd64 and arm64 architectures with improved artifact handling.
- Removed profile-specific inputs from the workflow, focusing on a unified build approach.
- Improved preseed file management and validation checks in the build script for better reliability.
Copy link

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 2

🤖 Fix all issues with AI agents
In @.github/workflows/build-iso.yml:
- Around line 238-246: The GitHub Actions step named "Create Release" is using
the outdated action reference softprops/action-gh-release@v1; update the uses
value to softprops/action-gh-release@v2, then verify the step's inputs (the
files multiline input) remain compatible with v2 and run the workflow to confirm
the release artifacts (release/*.iso, release/*.deb, release/SHA256SUMS,
release/SHA512SUMS, release/sbom/*) are still uploaded correctly.
🧹 Nitpick comments (11)
scripts/install-deps.sh (3)

14-16: Minor inconsistency with logging format.

The logging functions here use [+] and [!] prefixes, while scripts/build.sh uses [BUILD] and [WARN] prefixes. Consider aligning these for consistency across the build tooling.

Based on the relevant code snippets from scripts/build.sh:

log() { echo -e "${GREEN}[BUILD]${NC} $*"; }
warn() { echo -e "${YELLOW}[WARN]${NC} $*"; }
♻️ Optional: Align with build.sh format
-log() { echo -e "${GREEN}[+]${NC} $1"; }
-warn() { echo -e "${YELLOW}[!]${NC} $1"; }
-error() { echo -e "${RED}[ERROR]${NC} $1" >&2; }
+log() { echo -e "${GREEN}[DEPS]${NC} $1"; }
+warn() { echo -e "${YELLOW}[WARN]${NC} $1"; }
+error() { echo -e "${RED}[ERROR]${NC} $1" >&2; }

28-29: Consider quieting apt-get update output.

The apt-get update output can be verbose. Consider adding -qq for quieter output, or redirecting stdout while preserving stderr for errors.

♻️ Suggested improvement
 # Update package lists
-apt-get update
+apt-get update -qq

51-52: Add --no-install-recommends to reduce image size.

For build tooling, installing only the required packages without recommends can reduce disk usage and build time.

♻️ Suggested improvement
 log "Installing common packages..."
-apt-get install -y "${COMMON_PACKAGES[@]}"
+apt-get install -y --no-install-recommends "${COMMON_PACKAGES[@]}"
.github/workflows/build-iso.yml (2)

186-192: Checksum verification may silently skip ISOs without checksums.

The loop continues silently if a .sha256 file doesn't exist. Consider logging or failing explicitly if expected checksums are missing.

♻️ Suggested improvement
       - name: Verify checksums
         run: |
           ls -la
           for iso in *.iso; do
             if [ -f "${iso}.sha256" ]; then
               sha256sum -c "${iso}.sha256"
+            else
+              echo "Warning: No checksum file found for ${iso}"
             fi
           done

232-236: Checksum generation may fail if no .deb files exist.

If the branding package download fails (despite continue-on-error: true on SBOM), the sha256sum *.deb glob may fail. Consider adding a fallback.

♻️ Suggested improvement
       - name: Generate combined checksums
         run: |
           cd release
-          sha256sum *.iso *.deb > SHA256SUMS
-          sha512sum *.iso *.deb > SHA512SUMS
+          sha256sum *.iso > SHA256SUMS
+          sha256sum *.deb >> SHA256SUMS 2>/dev/null || true
+          sha512sum *.iso > SHA512SUMS
+          sha512sum *.deb >> SHA512SUMS 2>/dev/null || true
Makefile (2)

137-142: Root check implementation is correct but could be extracted.

The inline root check works, but duplicating this logic (also in install-deps.sh) could be consolidated into a shared helper if more targets require root.


152-160: Add security note for chroot-shell target.

Entering a chroot with sudo chroot is powerful. Consider adding a comment or warning that this is for development only and the chroot environment should not be trusted.

♻️ Optional: Add development-only warning
 # Enter interactive shell inside the chroot filesystem
+# WARNING: Development use only - chroot environment is not sanitized
 chroot-shell:
 	@if [ -d "$(BUILD_DIR)/chroot" ]; then \
+		echo "WARNING: Entering unsanitized chroot environment"; \
 		echo "Entering chroot..."; \
scripts/build.sh (4)

100-109: Live-build version comparison may fail if dpkg-query returns non-version string.

If dpkg-query fails or returns an unexpected format, the comparison could behave unexpectedly. The fallback to "0" is good, but consider also handling the case where dpkg --compare-versions itself fails.

♻️ Optional: Add defensive error handling
     if command -v lb &>/dev/null; then
         local lb_version
         lb_version=$(dpkg-query -W -f='${Version}' live-build 2>/dev/null || echo "0")
-        if dpkg --compare-versions "$lb_version" lt "1:20210814"; then
+        if dpkg --compare-versions "$lb_version" lt "1:20210814" 2>/dev/null; then
             warn "live-build version $lb_version may be too old. Recommended: >= 1:20210814"
         else
             log "live-build version: $lb_version"

659-662: Hardcoded package version should be parameterized.

The pkg_version="1.0.0" is hardcoded. Consider reading this from the DEBIAN/control file or making it configurable via environment variable for release versioning.

♻️ Suggested improvement
 cmd_branding_package() {
     local pkg_name="cortex-branding"
-    local pkg_version="1.0.0"
+    local pkg_version="${BRANDING_VERSION:-1.0.0}"
     local pkg_dir="${BUILD_DIR}/${pkg_name}"

Alternatively, extract version from the control file:

local pkg_version
pkg_version=$(grep '^Version:' "${PACKAGES_DIR}/${pkg_name}/DEBIAN/control" | cut -d' ' -f2)

602-612: cmd_clean uses sudo but doesn't verify build directory safety.

Before running sudo lb clean in an arbitrary directory, consider validating that BUILD_DIR is within the expected project structure to prevent accidental cleanup of unintended paths.

♻️ Add path validation
 cmd_clean() {
     header "Cleaning build"
 
     if [ -d "$BUILD_DIR" ]; then
+        # Ensure BUILD_DIR is within PROJECT_ROOT
+        case "$BUILD_DIR" in
+            "${PROJECT_ROOT}/"*) ;;
+            *) error "BUILD_DIR is outside project root"; return 1 ;;
+        esac
         cd "$BUILD_DIR"
         sudo lb clean
         log "Clean complete"

536-545: Consider adding sudo -n flag to fail fast if authentication is required.

While the build runs in a --privileged Docker container (which typically allows sudo without TTY), using sudo -n is defensive and will cause the build to fail immediately if sudo authentication is required, rather than hanging.

📜 Review details

Configuration used: defaults

Review profile: CHILL

Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between b071385 and 3b68a92.

📒 Files selected for processing (4)
  • .github/workflows/build-iso.yml
  • Makefile
  • scripts/build.sh
  • scripts/install-deps.sh
🧰 Additional context used
🧬 Code graph analysis (1)
scripts/install-deps.sh (1)
scripts/build.sh (3)
  • error (44-44)
  • log (42-42)
  • warn (43-43)
🪛 actionlint (1.7.10)
.github/workflows/build-iso.yml

239-239: the runner of "softprops/action-gh-release@v1" action is too old to run on GitHub Actions. update the action's version to fix this issue

(action)

🪛 checkmake (0.2.2)
Makefile

[warning] 43-43: Target body for "help" exceeds allowed length of 5 (32).

(maxbodylength)


[warning] 166-166: Target body for "config" exceeds allowed length of 5 (7).

(maxbodylength)


[warning] 14-14: Missing required phony target "clean"

(minphony)


[warning] 14-14: Missing required phony target "test"

(minphony)

⏰ Context from checks skipped due to timeout of 90000ms. You can increase the timeout in your CodeRabbit configuration to a maximum of 15 minutes (900000ms). (2)
  • GitHub Check: Build ISO (amd64)
  • GitHub Check: Build (First Pass)
🔇 Additional comments (9)
scripts/install-deps.sh (1)

55-64: LGTM on architecture handling.

Good conditional handling for amd64 and arm64 architectures with a sensible fallback warning for unknown architectures.

.github/workflows/build-iso.yml (1)

75-86: LGTM on matrix strategy.

Good use of fail-fast: false to allow both architectures to complete independently, and appropriate runner selection for arm64.

Makefile (3)

14-17: LGTM on .PHONY declaration.

The static analysis warning about missing clean and test phony targets appears to be a false positive—both are listed on line 15.


20-21: Good shell strictness settings.

Using -eu -o pipefail ensures errors are caught and pipelines fail properly. This is a best practice for robust Makefiles.


103-108: Consider adding validate dependency to iso-arm64 as well.

The iso target has check-deps validate as dependencies, but iso-arm64 only invokes $(MAKE) ARCH=arm64 iso. This is correct since it delegates to iso, but verify the recursive make call inherits the dependencies properly.

scripts/build.sh (4)

7-7: LGTM on strict mode.

Using set -euo pipefail ensures the script fails fast on errors, unset variables, and pipeline failures.


63-72: Glob expansion is intentionally unquoted.

The shellcheck disable comment correctly documents that the unquoted glob is intentional for pattern matching. Good use of the disable directive.


798-839: LGTM on main entry point.

Clean command dispatcher pattern with sensible defaults and help fallback for unknown commands.


286-295: Error accumulation pattern loses granularity.

Using || errors=$((errors + $?)) to capture exit codes is creative, but $? after || will always be 0 since the || clause succeeded. The validation functions return error counts directly, which works, but this pattern is fragile.

🐛 Issue explanation and fix

When you write validate_provision || errors=$((errors + $?)), the $? in the right-hand side reflects the exit status of the || expression itself (which is 0 if the right side executes). The actual error count from validate_provision is lost.

Consider capturing the return value explicitly:

         provision)
-            validate_provision || errors=$((errors + $?))
+            validate_provision || { local rc=$?; errors=$((errors + rc)); }
             ;;
         hooks)
-            validate_hooks || errors=$((errors + $?))
+            validate_hooks || { local rc=$?; errors=$((errors + rc)); }
             ;;
         lint)
-            run_shellcheck || errors=$((errors + $?))
+            run_shellcheck || { local rc=$?; errors=$((errors + rc)); }

Or restructure to use:

validate_provision; errors=$((errors + $?))

Likely an incorrect or invalid review comment.

- Removed obsolete preseed profiles (core, full, secops) to streamline configuration management.
- Updated the base preseed file to clarify its purpose and settings.
- Simplified the first-boot provisioning script by removing profile-specific logic and enhancing core setup functions.
- Improved logging and error handling in the provisioning script for better maintainability.
Copy link

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 13

🤖 Fix all issues with AI agents
In @iso/preseed/cortex-base.preseed:
- Around line 59-77: The preseed is missing the unattended-upgrades package so
first-boot.sh’s check for unattended-upgrades will always skip; add
"unattended-upgrades" to the d-i pkgsel/include string in the preseed (alongside
sudo, openssh-server, etc.) so the package is present on install, or
alternatively update the first-boot.sh patch that checks for
"unattended-upgrades" to attempt to install the package when online (i.e.,
detect missing package and run apt-get install unattended-upgrades before
configuring it).
- Around line 11-17: The preseed contains two hostname keys that conflict:
netcfg/get_hostname and netcfg/hostname; pick one canonical key and remove the
other to avoid ambiguity (e.g., keep netcfg/hostname string cortex and delete
the netcfg/get_hostname line, or vice versa), and ensure any downstream scripts
that read the hostname use the chosen key (update references if needed) so
preseeding yields a single authoritative hostname source.
- Around line 30-44: Replace the invalid placeholder in d-i
passwd/user-password-crypted with a real crypt(3) SHA-512 hash for a known
temporary password (so the installer accepts it), do not rely on
allow-password-weak as the only safeguard (keep or set d-i
user-setup/allow-password-weak appropriately), and ensure the account is forced
to rotate the password at first login by adding a late_command that runs chage
-d 0 for the cortex user (targeting the username "cortex") so the temporary
password cannot remain in use.
- Around line 96-103: The late_command uses semicolon-separated commands so
failures are ignored; replace the cp + in-target chmod sequence with atomic
installs using install -m to copy files with correct permissions (for example
install -m 0755 for first-boot.sh and appropriate mode for
cortex-first-boot.service), and chain commands with && (or explicit conditional
checks) so the chain aborts on error; ensure you still use in-target for
systemctl enable cortex-first-boot.service and make the overall
preseed/late_command fail-fast if any install or mkdir step fails so
provisioning errors are not silently ignored.

In @iso/provisioning/first-boot.sh:
- Around line 235-256: The setup_unattended_upgrades function is effectively a
no-op under the current preseed because unattended-upgrades isn't installed;
either ensure the package is installed before the check or remove the function
call. Modify setup_unattended_upgrades to invoke package installation (e.g.,
apt-get install -y unattended-upgrades) if command -v returns false, or delete
the setup_unattended_upgrades invocation and its helper if you intend not to
enable unattended upgrades by default; refer to the function name
setup_unattended_upgrades and the auto_upgrades variable to locate the relevant
code to change.
- Around line 107-114: The setup_password_change function currently only
enforces chage -d 0 when REQUIRE_PASSWORD_CHANGE is explicitly true, which can
leave installs with unchanged placeholder passwords; update
setup_password_change to default to forcing rotation (treat
REQUIRE_PASSWORD_CHANGE as true when unset) or additionally detect a known
temporary/placeholder hash for the cortex account and force chage -d 0 when that
hash is present; make changes to the function that reference
REQUIRE_PASSWORD_CHANGE, chage -d 0 and the cortex username, and ensure you log
the decision via log_info so the behavior is auditable.
- Around line 258-277: The setup_desktop function calls groups cortex and
usermod -aG docker cortex without checking the cortex user exists, which can
cause the script to fail under set -e; before invoking groups or usermod, guard
those calls with a check like getent passwd cortex (or id -u cortex) to ensure
the user exists, and only run groups and usermod when that check succeeds so
setup_desktop safely skips Docker group configuration if the cortex user is
missing.
- Around line 81-105: The sudo Defaults in setup_sudo are currently global;
modify the here-doc written to sudoers_file (variable sudoers_file and function
setup_sudo) so each Defaults line is scoped to the cortex user by prefixing with
"Defaults:cortex" (e.g., Defaults:cortex env_reset, Defaults:cortex
logfile=...), keep the cortex ALL=(ALL:ALL) ALL line as-is, then after writing
the file run a syntax check with visudo -cf "${sudoers_file}" before chmod 440
to ensure validity.
- Around line 58-67: The check_network() function currently invokes ping
directly which under set -e will cause the script to exit if ping is missing;
update check_network to first test for ping's existence (e.g., using command -v
ping or type ping) and if not found log_warn "Network: Offline (ping missing)"
and export NETWORK_AVAILABLE=false, otherwise run the existing ping check and
set NETWORK_AVAILABLE=true/false based on its result; ensure any invocation of
ping is guarded so a missing binary or non-zero exit does not trigger an
unexpected script termination.
- Around line 16-55: The script currently logs in check_already_complete and
mark_complete before ensuring LOG_DIR/LOG_FILE and PROVISION_DIR exist, causing
non-idempotent failures; modify startup to bootstrap directories and log file
first (mkdir -p "${LOG_DIR}" and "${PROVISION_DIR}", touch/chmod "${LOG_FILE}")
before any call to log or running check_already_complete, and update
mark_complete() to ensure PROVISION_DIR exists (mkdir -p "${PROVISION_DIR}")
before creating STATE_FILE so writing the state is safe and idempotent.
- Around line 318-336: In setup_ssh_keys(), the systemctl call uses the
Debian-incompatible unit name "sshd"; change the restart to use the canonical
"ssh" unit instead (i.e., replace the systemctl restart sshd || true call with
systemctl restart ssh || true) so the SSH service restart is reliable across
Debian/Ubuntu systems after generating host keys.
- Around line 116-168: The SSH hardening block in setup_ssh has a mismatched
comment for PasswordAuthentication, a non‑FIPS-first cipher order in Ciphers,
and uses the nonstandard service name sshd; update the comment near
PasswordAuthentication to reflect that it enables or disables password auth
globally (adjust text for PasswordAuthentication yes/no to match the actual
value of the setting), reorder the Ciphers line to list AES-GCM ciphers first
(e.g., aes256-gcm@openssh.com,aes128-gcm@openssh.com) or remove
chacha20-poly1305@openssh.com if FIPS compliance is required, and change the
systemctl checks/reload that reference sshd to use ssh (i.e., replace systemctl
is-active --quiet sshd and systemctl reload sshd with ssh) while keeping the
rest of setup_ssh and the sshd_config filename unchanged.
🧹 Nitpick comments (2)
iso/provisioning/first-boot.sh (2)

170-193: Use ufw --force enable instead of piping echo "y".

Less brittle under pipefail, and avoids interactive behavior changes across UFW versions.

Proposed change
-            echo "y" | ufw enable
+            ufw --force enable

370-403: Add an explicit root check early.

Right now, a non-root invocation will fail mid-script in less obvious places; fail fast with a clear log.

Proposed addition
 main() {
+    if [[ "${EUID}" -ne 0 ]]; then
+        echo "first-boot.sh must be run as root" >&2
+        exit 1
+    fi
📜 Review details

Configuration used: defaults

Review profile: CHILL

Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between 3b68a92 and 13953b2.

📒 Files selected for processing (2)
  • iso/preseed/cortex-base.preseed
  • iso/provisioning/first-boot.sh
🧰 Additional context used
🧬 Code graph analysis (1)
iso/provisioning/first-boot.sh (3)
repository/scripts/repo-manage.sh (1)
  • log (31-31)
scripts/build.sh (2)
  • log (42-42)
  • main (798-839)
branding/install-branding.sh (4)
  • log_info (22-22)
  • log_warn (24-24)
  • log_error (25-25)
  • main (213-242)
⏰ Context from checks skipped due to timeout of 90000ms. You can increase the timeout in your CodeRabbit configuration to a maximum of 15 minutes (900000ms). (2)
  • GitHub Check: Build ISO (amd64)
  • GitHub Check: Build (First Pass)

Comment on lines +16 to +55
set -euo pipefail

readonly CORTEX_DIR="/opt/cortex"
readonly PROVISION_DIR="${CORTEX_DIR}/provisioning"
readonly LOG_DIR="/var/log/cortex"
readonly LOG_FILE="${LOG_DIR}/first-boot.log"
readonly STATE_FILE="${PROVISION_DIR}/.first-boot-complete"

# Logging functions
log() {
local level="$1"
shift
local message="$*"
local timestamp
timestamp=$(date '+%Y-%m-%d %H:%M:%S')
echo "[${timestamp}] [${level}] ${message}" | tee -a "${LOG_FILE}"
}

log_info() { log "INFO" "$@"; }
log_warn() { log "WARN" "$@"; }
log_error() { log "ERROR" "$@"; }

# Check if already completed
check_already_complete() {
if [[ -f "${STATE_FILE}" ]]; then
log_info "First-boot provisioning already completed. Exiting."
exit 0
fi
}

# Initialize logging
init_logging() {
mkdir -p "${LOG_DIR}"
touch "${LOG_FILE}"
chmod 640 "${LOG_FILE}"
log_info "=== Cortex Linux First-Boot Provisioning Started ==="
log_info "Hostname: $(hostname)"
log_info "Date: $(date)"
log_info "Kernel: $(uname -r)"
}
Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Potential issue | 🟠 Major

Make idempotency truly safe: init logging/state dirs before any possible logging and before writing STATE_FILE.

Today, a “completed” run can still fail if ${LOG_DIR} or ${LOG_FILE} is missing (e.g., user cleanup) because check_already_complete logs before init_logging, and mark_complete assumes ${PROVISION_DIR} exists.

Proposed fix (directory bootstrap first, then state check)
 main() {
-    check_already_complete
-    init_logging
+    # Bootstrap dirs early so logging/state is safe even on "already complete" paths.
+    mkdir -p "${LOG_DIR}" "${PROVISION_DIR}"
+    touch "${LOG_FILE}"
+    chmod 640 "${LOG_FILE}"
+
+    check_already_complete
+    init_logging
     check_network

And in mark_complete():

 mark_complete() {
+    mkdir -p "${PROVISION_DIR}"
     local completion_time
🤖 Prompt for AI Agents
In @iso/provisioning/first-boot.sh around lines 16 - 55, The script currently
logs in check_already_complete and mark_complete before ensuring
LOG_DIR/LOG_FILE and PROVISION_DIR exist, causing non-idempotent failures;
modify startup to bootstrap directories and log file first (mkdir -p
"${LOG_DIR}" and "${PROVISION_DIR}", touch/chmod "${LOG_FILE}") before any call
to log or running check_already_complete, and update mark_complete() to ensure
PROVISION_DIR exists (mkdir -p "${PROVISION_DIR}") before creating STATE_FILE so
writing the state is safe and idempotent.

Comment on lines +116 to +168
# Configure SSH hardening
setup_ssh() {
log_info "Configuring SSH..."

local sshd_config="/etc/ssh/sshd_config.d/cortex-hardening.conf"

if [[ ! -f "${sshd_config}" ]]; then
mkdir -p /etc/ssh/sshd_config.d
cat > "${sshd_config}" << 'EOF'
# Cortex Linux SSH Hardening
# Disable root login
PermitRootLogin no
# Disable password authentication for root
PasswordAuthentication yes
PubkeyAuthentication yes
# Disable empty passwords
PermitEmptyPasswords no
# Limit authentication attempts
MaxAuthTries 3
# Disable X11 forwarding (enable if needed)
X11Forwarding no
# Client alive settings
ClientAliveInterval 300
ClientAliveCountMax 2
# Disable TCP forwarding by default
AllowTcpForwarding no
# Use strong ciphers only
Ciphers chacha20-poly1305@openssh.com,aes256-gcm@openssh.com,aes128-gcm@openssh.com
# Use strong MACs
MACs hmac-sha2-512-etm@openssh.com,hmac-sha2-256-etm@openssh.com
# Use strong key exchange
KexAlgorithms curve25519-sha256,curve25519-sha256@libssh.org
EOF
chmod 644 "${sshd_config}"
log_info "SSH hardening configured"

# Reload SSH if running
if systemctl is-active --quiet sshd; then
systemctl reload sshd || true
fi
else
log_info "SSH hardening already configured"
fi
}
Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Potential issue | 🟠 Major

🧩 Analysis chain

🏁 Script executed:

# Check if there's any documentation or context about Cortex Linux in the repo
find . -type f \( -name "*.md" -o -name "*.txt" -o -name "*.sh" \) | head -20 | xargs grep -l "cortex\|Cortex" 2>/dev/null | head -5

Repository: cortexlinux/cortex-distro

Length of output: 198


🌐 Web query:

Debian systemctl SSH service name ssh vs sshd openssh-server

💡 Result:

Short answer: On Debian and Ubuntu the unit provided by the openssh-server package is ssh.service (you should use systemctl start|stop|status ssh). sshd (the daemon binary) and its config remain named sshd/sshd_config; sshd.service is an alias for compatibility on Debian-derived systems. [1][2][3]

Useful commands:

  • Check unit: systemctl status ssh
  • Binary path: which sshd or ps aux | grep sshd
  • Config: /etc/ssh/sshd_config

Sources:
[1] Debian mailing list / openssh packaging (describes ssh.service, socket activation and aliasing).
[2] Ubuntu OpenSSH docs showing use of systemctl restart ssh.service and /etc/ssh/sshd_config.
[3] Discussion/examples showing ssh.service with Alias=sshd.service and note that sshd is the daemon program.


🌐 Web query:

openssh ciphers chacha20-poly1305 aes256-gcm aes128-gcm FIPS compliance compatibility

💡 Result:

Short answer

  • OpenSSH supports chacha20-poly1305@openssh.com and the AES‑GCM variants (aes128-gcm@openssh.com, aes256-gcm@openssh.com). (docs.gigamon.com)
  • ChaCha20‑Poly1305 is not NIST FIPS‑approved (and therefore is normally disallowed in FIPS mode / by FIPS crypto policies). AES‑GCM variants are accepted by FIPS policies and commonly allowed when the crypto provider is FIPS‑validated. (docs.redhat.com)
  • Practically this means: on a system or distro in FIPS mode (or using a system‑wide FIPS crypto policy) OpenSSH will not offer/use ChaCha20‑Poly1305; AES‑GCM (and other FIPS‑approved AES ciphers/CTR modes) should be used for FIPS compliance. System/distro policies or OpenSSH/FIPS configuration can also remove or reorder advertised ciphers, so explicit sshd_config/ssh_config entries may be required for compatibility. (docs.redhat.com)

Compatibility guidance (practical)

If you want, I can:

  • show the exact sshd_config/ssh_config lines to enforce AES‑GCM only, or
  • check how your OS/distribution’s FIPS policy will affect OpenSSH on a specific release (please tell me the OS and version).

Citations:


🏁 Script executed:

find . -type f \( -name "*.md" -o -name "*.txt" -o -name "README*" \) | xargs grep -i "fips\|compliance" 2>/dev/null | head -20

Repository: cortexlinux/cortex-distro

Length of output: 230


🏁 Script executed:

# Check the provisioning script context and any related hardening docs
ls -la iso/provisioning/ 2>/dev/null || ls -la . | head -20

Repository: cortexlinux/cortex-distro

Length of output: 322


🏁 Script executed:

# Look for any OS distro identification or FIPS mode checks in the script
cat iso/provisioning/first-boot.sh 2>/dev/null | head -50

Repository: cortexlinux/cortex-distro

Length of output: 1288


🏁 Script executed:

# Check for more context on the SSH setup function and security requirements
sed -n '116,168p' iso/provisioning/first-boot.sh | head -60

Repository: cortexlinux/cortex-distro

Length of output: 1362


🏁 Script executed:

# Look for any FIPS-related setup or security hardening docs in the repo
find . -type f -name "*.md" | xargs grep -i "ssh\|sshd\|hardening" 2>/dev/null | head -20

Repository: cortexlinux/cortex-distro

Length of output: 1168


🌐 Web query:

Cortex Linux distro FIPS compliance security requirements

💡 Result:

Do you mean (A) Palo Alto Networks’ Cortex cloud product, (B) a specific Linux distribution named “Cortex”, or (C) running a Linux system on ARM Cortex processors? I can proceed once you confirm.

Meanwhile, here are the concise, general FIPS compliance security requirements for Linux distributions (what vendors follow and you must implement to claim FIPS compliance):

  • Use NIST‑validated cryptographic modules (CMVP certificates) for all crypto operations; only the validated module versions and configurations listed in the module’s security policy may be used. [1][2][3]
  • Enable “FIPS mode” at installation (or follow the vendor’s supported FIPS enablement procedure). Enabling FIPS after installing workloads may not guarantee compliance; many vendors require enabling during install or immediate post‑install steps and regenerating keys. [2][4][5]
  • Restrict algorithms/ciphers to FIPS‑approved algorithms and approved protocol parameters (TLS suites, key sizes, KDFs, RNGs) and enforce system‑wide crypto policies so non‑approved ciphers are not offered. [4][5]
  • Use the exact package versions of crypto libraries/modules that were validated (and keep to vendor guidance about updates/patch channels for validated packages). [6][3]
  • Ensure kernel/OS module boundaries meet FIPS module definitions (some distros implement a kernel FIPS object module with self‑tests and early loading). Follow distro-specific kernel guidance. [7][4]
  • Perform and preserve self‑tests (power‑up, conditional) and continuous integrity checks required by the validated module’s security policy. [1][7]
  • Protect key material lifecycle: generate keys in FIPS mode, avoid exporting/using keys in non‑FIPS contexts unless allowed by policy (wrapping rules). [2][4]
  • Maintain documentation and evidence (security policy, validated module certificate numbers, tested configuration) for audits and for CMVP mapping. [1][6]
  • Follow vendor/distro-specific caveats and interoperability notes (e.g., TLS EMS requirements, disabled algorithms, compatibility impacts). Test workloads for functionality in FIPS mode. [4][5]

If you want, I can:

  • Check whether a specific “Cortex” product/distribution has an assigned CMVP certificate and its security policy (and cite the exact certificate and requirements).
  • Provide a step‑by‑step FIPS enablement checklist for a given distro (RHEL/Ubuntu/SLES/Oracle).

Which “Cortex” did you mean?

Sources: NIST CMVP and vendor FIPS docs (examples): [1] NIST CMVP certificate listing; [2] Red Hat FIPS docs; [3] Oracle Linux FIPS modules; [4] Linux kernel FIPS blog; [5] SUSE FIPS guidance; [6] Ubuntu FIPS announcement.


Fix comment mismatch, reorder crypto preferences for FIPS compliance, and use standard ssh service name.

  • Comment states "Disable password authentication for root" but PasswordAuthentication yes applies globally (enables password auth). Update the comment to reflect the actual setting.
  • Listing chacha20-poly1305@openssh.com first breaks FIPS compliance (ChaCha20-Poly1305 is not FIPS-approved). If FIPS compliance is a requirement, reorder to list only AES-GCM ciphers, or conditionally include ChaCha20 with documentation. If FIPS is not required, clarify this in a comment.
  • Use ssh.service instead of sshd.service to match Debian/Ubuntu conventions (the openssh-server package provides ssh.service; sshd.service is an alias for compatibility).
Suggested fixes
-# Disable password authentication for root
-PasswordAuthentication yes
+# Password authentication is enabled globally (set to "no" for key-only SSH)
+PasswordAuthentication yes

Reorder ciphers to prioritize FIPS-approved algorithms:

-Ciphers chacha20-poly1305@openssh.com,aes256-gcm@openssh.com,aes128-gcm@openssh.com
+Ciphers aes256-gcm@openssh.com,aes128-gcm@openssh.com,chacha20-poly1305@openssh.com

Use standard ssh service name:

-        if systemctl is-active --quiet sshd; then
-            systemctl reload sshd || true
+        if systemctl is-active --quiet ssh; then
+            systemctl reload ssh || true
         fi
🤖 Prompt for AI Agents
In @iso/provisioning/first-boot.sh around lines 116 - 168, The SSH hardening
block in setup_ssh has a mismatched comment for PasswordAuthentication, a
non‑FIPS-first cipher order in Ciphers, and uses the nonstandard service name
sshd; update the comment near PasswordAuthentication to reflect that it enables
or disables password auth globally (adjust text for PasswordAuthentication
yes/no to match the actual value of the setting), reorder the Ciphers line to
list AES-GCM ciphers first (e.g., aes256-gcm@openssh.com,aes128-gcm@openssh.com)
or remove chacha20-poly1305@openssh.com if FIPS compliance is required, and
change the systemctl checks/reload that reference sshd to use ssh (i.e., replace
systemctl is-active --quiet sshd and systemctl reload sshd with ssh) while
keeping the rest of setup_ssh and the sshd_config filename unchanged.

Comment on lines +235 to +256
# Configure automatic security updates
setup_unattended_upgrades() {
log_info "Configuring automatic security updates..."

if command -v unattended-upgrade &>/dev/null; then
local auto_upgrades="/etc/apt/apt.conf.d/20auto-upgrades"

if [[ ! -f "${auto_upgrades}" ]]; then
cat > "${auto_upgrades}" << 'EOF'
APT::Periodic::Update-Package-Lists "1";
APT::Periodic::Unattended-Upgrade "1";
APT::Periodic::Download-Upgradeable-Packages "1";
APT::Periodic::AutocleanInterval "7";
EOF
log_info "Automatic security updates enabled"
else
log_info "Automatic updates already configured"
fi
else
log_warn "unattended-upgrades not installed, skipping"
fi
}
Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Potential issue | 🟡 Minor

Either install unattended-upgrades or remove this step (currently a no-op in the default package set).

Given the preseed package list, this will almost always log “not installed, skipping.”

🤖 Prompt for AI Agents
In @iso/provisioning/first-boot.sh around lines 235 - 256, The
setup_unattended_upgrades function is effectively a no-op under the current
preseed because unattended-upgrades isn't installed; either ensure the package
is installed before the check or remove the function call. Modify
setup_unattended_upgrades to invoke package installation (e.g., apt-get install
-y unattended-upgrades) if command -v returns false, or delete the
setup_unattended_upgrades invocation and its helper if you intend not to enable
unattended upgrades by default; refer to the function name
setup_unattended_upgrades and the auto_upgrades variable to locate the relevant
code to change.

Comment on lines +258 to +277
# Desktop environment setup
setup_desktop() {
log_info "Configuring desktop environment..."

# Add Flathub repository if flatpak is installed
if command -v flatpak &>/dev/null && [[ "${NETWORK_AVAILABLE}" == "true" ]]; then
if ! flatpak remote-list | grep -q flathub; then
flatpak remote-add --if-not-exists flathub https://flathub.org/repo/flathub.flatpakrepo || true
log_info "Flathub repository added"
fi
fi

# Configure Docker for cortex user
if command -v docker &>/dev/null; then
if ! groups cortex | grep -q docker; then
usermod -aG docker cortex
log_info "Added cortex user to docker group"
fi
fi
}
Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Potential issue | 🟠 Major

Guard groups/usermod with an existence check for the cortex user.

If the user creation ever changes (or fails), groups cortex/usermod will hard-fail provisioning under set -e.

Proposed guard
     # Configure Docker for cortex user
     if command -v docker &>/dev/null; then
-        if ! groups cortex | grep -q docker; then
-            usermod -aG docker cortex
-            log_info "Added cortex user to docker group"
-        fi
+        if id -u cortex &>/dev/null; then
+            if ! id -nG cortex | tr ' ' '\n' | grep -qx docker; then
+                usermod -aG docker cortex
+                log_info "Added cortex user to docker group"
+            fi
+        else
+            log_warn "User 'cortex' not found; skipping docker group setup"
+        fi
     fi
🤖 Prompt for AI Agents
In @iso/provisioning/first-boot.sh around lines 258 - 277, The setup_desktop
function calls groups cortex and usermod -aG docker cortex without checking the
cortex user exists, which can cause the script to fail under set -e; before
invoking groups or usermod, guard those calls with a check like getent passwd
cortex (or id -u cortex) to ensure the user exists, and only run groups and
usermod when that check succeeds so setup_desktop safely skips Docker group
configuration if the cortex user is missing.

Comment on lines +279 to +316
# Add Cortex repository (requires network)
setup_cortex_repository() {
if [[ "${NETWORK_AVAILABLE}" != "true" ]]; then
log_warn "Network unavailable, skipping Cortex repository setup"
return 0
fi

log_info "Setting up Cortex package repository..."

local keyring_dir="/usr/share/keyrings"
local sources_file="/etc/apt/sources.list.d/cortex-linux.list"

# Check if already configured (from live image or previous run)
if [[ -f "${sources_file}" ]]; then
log_info "Cortex repository already configured"
return 0
fi

log_info "Adding Cortex Linux APT repository..."

# Download and install GPG key
if curl -fsSL https://apt.cortexlinux.com/keys/cortex-linux.gpg.asc | gpg --dearmor -o "${keyring_dir}/cortex-linux.gpg"; then
log_info "Cortex GPG key installed"
else
log_error "Failed to download Cortex GPG key"
return 1
fi

# Add repository source
echo "deb [signed-by=${keyring_dir}/cortex-linux.gpg] https://apt.cortexlinux.com stable main" > "${sources_file}"

# Update package lists
if apt-get update -qq; then
log_info "Cortex repository configured successfully"
else
log_warn "apt-get update failed, repository may not be accessible"
fi
}
Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Potential issue | 🔴 Critical

🧩 Analysis chain

🌐 Web query:

/usr/share/keyrings directory Debian Ubuntu default exists standard

💡 Result:

Short answer: No — /usr/share/keyrings is not guaranteed to be present on a fresh Debian/Ubuntu install; it is the conventional location for keyring files that are installed and managed by packages, and packages that provide keyrings will place files there. For locally-managed repository keys you should use /etc/apt/keyrings instead. [1][2][3]

Sources:

  1. Debian Wiki — DebianKeyring (notes that installed keyrings are placed in /usr/share/keyrings). [Debian Wiki]. [1]
  2. Debian Wiki — UseThirdParty / guidance: /usr/share/keyrings for package-managed keys, /etc/apt/keyrings for locally managed keys. [2]
  3. APT notes / documentation explaining how /usr/share/keyrings is used (ignored by default unless referenced with signed-by). [3]

🏁 Script executed:

# Check if there are other instances of keyring setup in the codebase
rg -n "keyrings|gpg.*--dearmor" iso/provisioning/ --type sh

Repository: cortexlinux/cortex-distro

Length of output: 313


🏁 Script executed:

# Check if there are other repository setup examples in the script
rg -n "apt-key|sources.list|signed-by" iso/provisioning/first-boot.sh -A 3 -B 3

Repository: cortexlinux/cortex-distro

Length of output: 618


🌐 Web query:

/etc/apt/keyrings directory Debian Ubuntu APT local repository keys exists

💡 Result:

Short answer: Yes — /etc/apt/keyrings is the recommended place for locally-managed APT repository OpenPGP key files (keys managed by you, not by a package). Newer APT (since ~2.4) uses this location and you should reference the key with the signed-by= option in your source entry. On older Debian/Ubuntu releases the directory may not exist by default; create it (mode 0755) and place dearmoured .gpg key files there. [1][2][3]

Useful references:

  • Debian guidance: Use /etc/apt/keyrings for locally-managed keys (use /usr/share/keyrings for keys installed by packages). [1]
  • Debian SecureApt / apt-key deprecation details. [2]
  • Ubuntu/apt manpage and examples showing /etc/apt/keyrings + signed-by usage. [3]

🌐 Web query:

apt-get GPG key file permissions requirements 0644 readable

💡 Result:

apt (and apt-key/apt-key-like checks) requires repository public-key files to be readable by the unprivileged _apt user. If a key file in /etc/apt/trusted.gpg or /etc/apt/trusted.gpg.d/ is not world- or _apt-readable apt will ignore it and you'll see messages like "the key(s) ... are ignored as the file is not readable by user '_apt'". Recommended permissions are 0644 (rw‑r--r--) with root ownership (or root:root); 0644 makes the file readable by _apt. Alternatively you may set owner to _apt so only that user needs read (chown _apt:root). Example fixes:

  • Make file world-readable:
    sudo chmod 644 /etc/apt/trusted.gpg.d/NAME.gpg

  • Or make it readable by _apt:
    sudo chown _apt:root /etc/apt/trusted.gpg.d/NAME.gpg
    sudo chmod 640 /etc/apt/trusted.gpg.d/NAME.gpg

Note: current best practice is to store third‑party keys under /etc/apt/keyrings or /usr/share/keyrings and use the signed-by option in your sources, instead of putting many keys into /etc/apt/trusted.gpg.d. (blog.cynexia.com)

Citations:


🏁 Script executed:

# Check if there are other uses of /etc/apt or keyrings in the codebase
rg -n "/etc/apt" iso/provisioning/ --type sh

Repository: cortexlinux/cortex-distro

Length of output: 271


Use /etc/apt/keyrings for locally-managed repository keys, not /usr/share/keyrings — and ensure directory and key file have correct permissions.

The current code uses /usr/share/keyrings, which is reserved for package-managed keys per Debian guidelines. Locally-downloaded keys should go in /etc/apt/keyrings. Additionally, create the directory with proper permissions and ensure the key file is readable by the _apt user.

Proposed fix
-    local keyring_dir="/usr/share/keyrings"
+    local keyring_dir="/etc/apt/keyrings"
     local sources_file="/etc/apt/sources.list.d/cortex-linux.list"
+    mkdir -p "${keyring_dir}"
+    chmod 0755 "${keyring_dir}"
 
     # Download and install GPG key
     if curl -fsSL https://apt.cortexlinux.com/keys/cortex-linux.gpg.asc | gpg --dearmor -o "${keyring_dir}/cortex-linux.gpg"; then
         log_info "Cortex GPG key installed"
+        chmod 0644 "${keyring_dir}/cortex-linux.gpg"
     else
         log_error "Failed to download Cortex GPG key"
         return 1

Comment on lines +318 to +336
# Generate SSH host keys if missing
setup_ssh_keys() {
log_info "Checking SSH host keys..."

local keys_generated=false

for keytype in rsa ecdsa ed25519; do
local keyfile="/etc/ssh/ssh_host_${keytype}_key"
if [[ ! -f "${keyfile}" ]]; then
ssh-keygen -t "${keytype}" -f "${keyfile}" -N "" -q
keys_generated=true
log_info "Generated SSH ${keytype} host key"
fi
done

if [[ "${keys_generated}" == "true" ]]; then
systemctl restart sshd || true
fi
}
Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Potential issue | 🟡 Minor

🧩 Analysis chain

🌐 Web query:

Debian SSH systemd service name sshd vs ssh unit

💡 Result:

Short answer

  • On Debian the packaged systemd unit is named ssh.service (not sshd.service); the daemon binary remains /usr/sbin/sshd and config files are still sshd_config. [1][2]
  • Debian also ships socket/template units ssh.socket and ssh@.service for socket-activated or traditional modes. Some recent/unreleased changes add an sshd@.service template in experimental/sid. [1][2][4]

What that means for you

  • Use systemctl start|stop|restart ssh (or manage ssh.socket / ssh@.service for socket activation). systemctl restart sshd may not exist on some Debian/Ubuntu installs. [1][2]
  • The “sshd” names are still used for the binary, configs and PAM files (sshd_config, /etc/pam.d/sshd). Renaming the unit to ssh is a packaging/compatibility choice. [2][5]

References
[1] Debian openssh-server file list (shows ssh.service, ssh.socket, ssh@.service).
[2] Debian mailing-list discussion / systemd support in openssh-server.
[3] Ask Ubuntu explanation that Debian/Ubuntu use ssh.service (not sshd).
[4] Debian bug/changelog showing addition of sshd@.service template in newer packaging.
[5] Debian package file lists showing /usr/sbin/sshd and sshd_config.


🌐 Web query:

systemctl restart sshd Debian compatibility service alias

💡 Result:

Short answer: On Debian the canonical systemd unit is ssh.service; sshd.service is provided as a compatibility alias so "systemctl restart sshd" usually works. Use ssh.service when creating drop-ins or editing the unit (override files must use the real unit name). [1][2][3]

Sources:

  • Debian Handbook — ssh.service unit (includes "Alias=sshd.service"). [1]
  • Debian Wiki (systemd guidance: use canonical service name). [2]
  • Debian mailing-list note: sshd.service is an alias for ssh.service (implications for drop‑ins). [3]

🏁 Script executed:

cat -n iso/provisioning/first-boot.sh | head -50

Repository: cortexlinux/cortex-distro

Length of output: 1638


🏁 Script executed:

# Check for any OS detection or Debian-specific logic in the script
rg -i "debian|ubuntu|systemd" iso/provisioning/first-boot.sh -B 2 -A 2

Repository: cortexlinux/cortex-distro

Length of output: 195


Use the correct SSH systemd unit name for Debian: ssh.service instead of sshd.

On Debian, the canonical systemd unit is ssh.service, not sshd. While sshd is provided as a compatibility alias on most installs, it may not exist on all Debian/Ubuntu systems, causing the restart to fail silently or error. Use ssh.service for consistency and reliability:

systemctl restart ssh || true

🤖 Prompt for AI Agents
In @iso/provisioning/first-boot.sh around lines 318 - 336, In setup_ssh_keys(),
the systemctl call uses the Debian-incompatible unit name "sshd"; change the
restart to use the canonical "ssh" unit instead (i.e., replace the systemctl
restart sshd || true call with systemctl restart ssh || true) so the SSH service
restart is reliable across Debian/Ubuntu systems after generating host keys.

- Updated README.md to streamline installation instructions and clarify build commands for ISO and dependencies.
- Removed obsolete references to specific profiles in the automated installation guide, consolidating to a single preseed file.
- Enhanced the developers' guide with clearer instructions for building ISOs and managing package lists.
- Introduced a new package list for complete system installations, replacing the removed core profile.
- Improved overall documentation structure for better clarity and usability.
Copy link

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 3

🤖 Fix all issues with AI agents
In @docs/DEVELOPERS.md:
- Around line 137-142: The QEMU ARM64 example is missing firmware specification
causing boot failures; update the qemu-system-aarch64 invocation to add a
firmware flag (use either -bios or -pflash) and point it to the typical UEFI
firmware file (e.g., edk2-aarch64-code.fd), for example modify the
qemu-system-aarch64 command shown so it includes "-bios edk2-aarch64-code.fd"
(or "-pflash <path-to-edk2-aarch64-code.fd>") alongside the existing -M, -cpu,
-m and -cdrom options to ensure the VM can boot the ISO.

In @README.md:
- Around line 39-43: Update the README examples to reflect the date-based ISO
versioning used by ISO_VERSION: replace the hardcoded
cortex-linux-0.1.0-amd64.iso examples with a date-based pattern like
cortex-linux-YYYYMMDD-amd64.iso (e.g., cortex-linux-20250112-amd64.iso) or add a
note that the ISO name is generated from ISO_VERSION (date format), while
leaving the SBOM examples (which use CORTEX_VERSION and default to 0.1.0)
unchanged.
🧹 Nitpick comments (4)
docs/automated-installation.md (1)

25-27: Add language specifiers to fenced code blocks.

Several code blocks are missing language specifiers, which affects syntax highlighting and accessibility tools. Per static analysis hints, add appropriate language identifiers.

Suggested fixes

Line 25: Add language specifier for kernel boot parameters

-```
+```text
 preseed/file=/cdrom/preseed/cortex.preseed

Line 66: Add language specifier for preseed syntax

-```
+```text
 d-i partman-auto/expert_recipe string \

Line 109: Add language specifier for DHCP config

-```
+```text
 filename "pxelinux.0";

Line 131: Add language specifier for PXE config

-```
+```text
 DEFAULT cortex

Line 216: Add language specifier for preseed

-```
+```text
 d-i netcfg/disable_autoconfig boolean true

Also applies to: 66-74, 109-112, 131-136, 216-222

docs/DEVELOPERS.md (1)

208-212: Consider adding automated link verification to CI.

The Resources section links to external Debian documentation. Consider implementing periodic link validation in the CI/build process to catch broken references early, though this is optional.

README.md (2)

93-95: Clarify what's included versus what needs additional installation.

The terms "GPU support prerequisites" and "AI/ML prerequisites" are ambiguous. Consider specifying whether:

  • GPU drivers (NVIDIA/AMD) are pre-installed, or just kernel modules/dependencies
  • AI/ML packages (numpy, scipy, pandas) are pre-installed, or users need to install them post-boot

Suggestion: Use consistent language like "GPU driver support for NVIDIA and AMD" or "Pre-installed AI/ML libraries (numpy, scipy, pandas)" to clarify what's actually in the ISO.

📝 Proposed clarification
-- GPU support prerequisites (NVIDIA, AMD)
+- GPU driver support for NVIDIA and AMD hardware
 - Modern CLI tools (htop, btop, fzf, ripgrep, bat)
-- AI/ML prerequisites (numpy, scipy, pandas)
+- Pre-installed AI/ML libraries (numpy, scipy, pandas)

48-72: Consider adding a language specifier to the directory tree code block.

The static analysis tool flagged the fenced code block for missing a language specifier. While this is a minor issue, adding text or plaintext after the opening backticks would resolve the warning and improve Markdown compliance.

📝 Proposed fix for Markdown compliance
-```
+```text
 cortex-distro/
 ├── iso/                        # ISO build configuration

Based on static analysis hints from markdownlint-cli2.

📜 Review details

Configuration used: defaults

Review profile: CHILL

Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between 13953b2 and aa34a92.

📒 Files selected for processing (6)
  • README.md
  • docs/DEVELOPERS.md
  • docs/QUICK-START.md
  • docs/automated-installation.md
  • iso/live-build/config/package-lists/cortex-core.list.chroot
  • iso/live-build/config/package-lists/cortex.list.chroot
💤 Files with no reviewable changes (1)
  • iso/live-build/config/package-lists/cortex-core.list.chroot
🧰 Additional context used
🪛 LanguageTool
docs/automated-installation.md

[grammar] ~177-~177: Ensure spelling is correct
Context: ...rd:** Must be set at first login > The preseed uses a placeholder password hash. The f...

(QB_NEW_EN_ORTHOGRAPHY_ERROR_IDS_1)

🪛 markdownlint-cli2 (0.18.1)
README.md

56-56: Fenced code blocks should have a language specified

(MD040, fenced-code-language)

docs/automated-installation.md

25-25: Fenced code blocks should have a language specified

(MD040, fenced-code-language)


66-66: Fenced code blocks should have a language specified

(MD040, fenced-code-language)


109-109: Fenced code blocks should have a language specified

(MD040, fenced-code-language)


131-131: Fenced code blocks should have a language specified

(MD040, fenced-code-language)


216-216: Fenced code blocks should have a language specified

(MD040, fenced-code-language)

⏰ Context from checks skipped due to timeout of 90000ms. You can increase the timeout in your CodeRabbit configuration to a maximum of 15 minutes (900000ms). (2)
  • GitHub Check: Build ISO (amd64)
  • GitHub Check: Build (First Pass)
🔇 Additional comments (17)
docs/DEVELOPERS.md (2)

1-33: Clear and well-structured setup instructions.

The prerequisites section provides both automated (install-deps.sh) and manual installation paths, which is helpful for different user preferences. The version verification step for live-build is a good practice.


54-81: Project structure documentation is comprehensive.

The tree structure clearly documents the repository layout, including the new branding, provisioning, and live-build components introduced in this PR. This aligns well with the modular build system changes.

iso/live-build/config/package-lists/cortex.list.chroot (3)

233-238: Cortex package placeholders are appropriately commented.

The commented placeholders for cortex-archive-keyring, cortex-core, and cortex-console correctly indicate these will be enabled when the Cortex APT repository is available. This aligns with the modular branding approach documented in the PR.


1-14: LGTM on the refactored system base section.

The reorganization from "INCLUDE CORE" to "SYSTEM BASE" with explicit essential system utilities improves clarity. The package list now clearly defines the foundational system components.


151-157: No action needed. nvidia-smi is available as a standalone package in Debian's non-free archive and can be installed independently. The package is properly listed.

Likely an incorrect or invalid review comment.

docs/automated-installation.md (2)

76-101: First-boot provisioning documentation is thorough.

The documentation clearly explains idempotency, offline capability, state tracking location (/opt/cortex/provisioning/.first-boot-complete), and log locations. This aligns with the provisioning scripts introduced in iso/provisioning/.


230-236: Security considerations section provides essential guidance.

The recommendations for SSH key regeneration, system updates, firewall review, and fail2ban configuration are appropriate for production deployments.

docs/QUICK-START.md (4)

1-4: Clear and concise introduction.

The "2 minutes" promise sets appropriate expectations for the repository installation path. The guide effectively provides two clear installation options.


36-42: System requirements are reasonable.

The minimum (2GB RAM, 10GB disk) and recommended (4GB+ RAM, 20GB+ disk) requirements are appropriate for a development-oriented Linux distribution with container and AI/ML tooling.


86-89: Cross-references to related documentation are helpful.

The links to automated-installation.md and DEVELOPERS.md provide clear next steps for users who need more advanced workflows.


7-17: Verify APT repository URL is correct and accessible.

The documentation references https://apt.cortexlinux.com for the GPG key and repository installation. Ensure this domain is properly configured and both the /keys/cortex-linux.gpg.asc and repository endpoints are accessible, or update to the correct URL if different.

README.md (6)

46-72: LGTM! Clear and well-organized architecture documentation.

The directory structure accurately reflects the new branding-centric organization and clearly documents the preseed/provisioning infrastructure for automated installations.


161-171: LGTM! Build targets are clear and well-documented.

The simplified build target documentation aligns well with the streamlined build workflow. The explicit ARM64 target and install-deps convenience target improve usability.


196-196: Good addition of ARM64 architecture support.

Explicitly documenting ARM64 support aligns with the make iso ARCH=arm64 build target and makes hardware requirements clear for users.


134-134: Verify the cortex-branding package path and version.

The example references packages/cortex-branding_1.0.0_all.deb. Confirm:

  1. The package directory structure matches this path
  2. The version 1.0.0 is intentional (differs from the ISO version 0.1.0)
  3. The package naming convention follows Debian standards (package_version_arch.deb)

103-103: Preseed file is properly included.

The preseed file exists at iso/preseed/cortex.preseed and is tracked in the repository, so the boot parameter reference at line 103 is correct and will be properly included in the ISO build.


24-25: The scripts/install-deps.sh script exists with executable permissions set correctly. No action required.

Comment on lines +147 to +155
qemu-system-x86_64 \
-enable-kvm \
-m 4096 \
-cpu host \
-cdrom cortex-linux-*.iso \
-drive file=cortex.qcow2,format=qcow2 \
-boot d \
-append "auto=true priority=critical preseed/file=/cdrom/preseed/cortex.preseed"
```
Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Potential issue | 🟡 Minor

QEMU -append flag requires -kernel to function.

The -append parameter only works when booting with -kernel directly, not when booting from an ISO via -cdrom. For ISO-based installation with preseed, the boot parameters need to be edited interactively at the GRUB menu or baked into the ISO's bootloader configuration.

Suggested documentation fix
 # Boot from ISO with preseed
 qemu-system-x86_64 \
     -enable-kvm \
     -m 4096 \
     -cpu host \
     -cdrom cortex-linux-*.iso \
     -drive file=cortex.qcow2,format=qcow2 \
-    -boot d \
-    -append "auto=true priority=critical preseed/file=/cdrom/preseed/cortex.preseed"
+    -boot d
+
+# At the GRUB menu, edit the boot entry and append:
+# auto=true priority=critical preseed/file=/cdrom/preseed/cortex.preseed
📝 Committable suggestion

‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.

Suggested change
qemu-system-x86_64 \
-enable-kvm \
-m 4096 \
-cpu host \
-cdrom cortex-linux-*.iso \
-drive file=cortex.qcow2,format=qcow2 \
-boot d \
-append "auto=true priority=critical preseed/file=/cdrom/preseed/cortex.preseed"
```
qemu-system-x86_64 \
-enable-kvm \
-m 4096 \
-cpu host \
-cdrom cortex-linux-*.iso \
-drive file=cortex.qcow2,format=qcow2 \
-boot d
# At the GRUB menu, edit the boot entry and append:
# auto=true priority=critical preseed/file=/cdrom/preseed/cortex.preseed

Comment on lines +137 to +142
# QEMU (amd64)
qemu-system-x86_64 -m 4G -cdrom output/cortex-linux-*.iso -boot d

# QEMU (arm64)
qemu-system-aarch64 -M virt -cpu cortex-a72 -m 4G -cdrom output/cortex-linux-*-arm64.iso
```
Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Potential issue | 🟡 Minor

QEMU arm64 command may not boot correctly without a firmware path.

The qemu-system-aarch64 command for ARM64 testing is missing the -bios or -pflash flag to specify UEFI firmware (typically edk2-aarch64-code.fd). Without this, the VM may fail to boot the ISO.

Suggested fix
 # QEMU (arm64)
-qemu-system-aarch64 -M virt -cpu cortex-a72 -m 4G -cdrom output/cortex-linux-*-arm64.iso
+qemu-system-aarch64 -M virt -cpu cortex-a72 -m 4G \
+  -bios /usr/share/qemu-efi-aarch64/QEMU_EFI.fd \
+  -cdrom output/cortex-linux-*-arm64.iso -boot d
🤖 Prompt for AI Agents
In @docs/DEVELOPERS.md around lines 137 - 142, The QEMU ARM64 example is missing
firmware specification causing boot failures; update the qemu-system-aarch64
invocation to add a firmware flag (use either -bios or -pflash) and point it to
the typical UEFI firmware file (e.g., edk2-aarch64-code.fd), for example modify
the qemu-system-aarch64 command shown so it includes "-bios
edk2-aarch64-code.fd" (or "-pflash <path-to-edk2-aarch64-code.fd>") alongside
the existing -M, -cpu, -m and -cdrom options to ensure the VM can boot the ISO.

Comment on lines +39 to +43
├── cortex-linux-0.1.0-amd64.iso # Bootable ISO
├── cortex-linux-0.1.0-amd64.iso.sha256
└── sbom/
├── cortex-linux-0.1.0.cdx.json # CycloneDX SBOM
└── cortex-linux-0.1.0.spdx.json # SPDX SBOM
├── cortex-linux-0.1.0.cdx.json # CycloneDX SBOM
└── cortex-linux-0.1.0.spdx.json # SPDX SBOM
Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Potential issue | 🟠 Major

🧩 Analysis chain

🏁 Script executed:

# Search for version references in build configuration and common config files
rg -n 'version\s*[=:]\s*["\']?0\.1\.0' -i --max-count=20

Repository: cortexlinux/cortex-distro

Length of output: 137


🏁 Script executed:

# Look for version definitions in common configuration files
fd -e 'py|toml|json|yaml|yml|sh|Makefile|mk|cfg|ini' --max-depth 3 | head -20

Repository: cortexlinux/cortex-distro

Length of output: 51


🏁 Script executed:

# Check the README.md file for version context
cat -n README.md | sed -n '30,50p'

Repository: cortexlinux/cortex-distro

Length of output: 632


🏁 Script executed:

# Search for version definitions without quote issues
rg -n '0\.1\.0' --max-count=30

Repository: cortexlinux/cortex-distro

Length of output: 1199


🏁 Script executed:

# Search for VERSION variable definitions
rg -n 'VERSION\s*=' --max-count=20

Repository: cortexlinux/cortex-distro

Length of output: 800


🏁 Script executed:

# List top-level files to identify build config
ls -la | grep -E 'Makefile|setup|version|VERSION'

Repository: cortexlinux/cortex-distro

Length of output: 127


🏁 Script executed:

# Check if there's a Makefile and search for version there
if [ -f Makefile ]; then head -50 Makefile; fi

Repository: cortexlinux/cortex-distro

Length of output: 1483


🏁 Script executed:

# Check Makefile for how ISO_VERSION and ISO_NAME are used
cat -n Makefile | grep -A 5 -B 5 'ISO_NAME\|ISO_VERSION'

Repository: cortexlinux/cortex-distro

Length of output: 1880


🏁 Script executed:

# Check the build.sh script to see actual ISO filename generation
rg -n 'ISO_NAME|ISO_VERSION' scripts/build.sh -A 2 -B 2

Repository: cortexlinux/cortex-distro

Length of output: 727


Update README.md output examples to reflect actual versioning scheme.

The documented ISO filename examples in lines 39-43 are inaccurate. The build system uses a date-based version format (YYYYMMDD) for ISO_VERSION, not the hardcoded 0.1.0 shown in the README. The actual ISO output will be cortex-linux-YYYYMMDD-amd64.iso (e.g., cortex-linux-20250112-amd64.iso), not cortex-linux-0.1.0-amd64.iso. Update the documentation examples to match the actual build output, or clearly indicate these are example filenames only.

Note: SBOM filenames default to 0.1.0 via CORTEX_VERSION, so those examples are correct; only the ISO filename examples require updating.

🤖 Prompt for AI Agents
In @README.md around lines 39 - 43, Update the README examples to reflect the
date-based ISO versioning used by ISO_VERSION: replace the hardcoded
cortex-linux-0.1.0-amd64.iso examples with a date-based pattern like
cortex-linux-YYYYMMDD-amd64.iso (e.g., cortex-linux-20250112-amd64.iso) or add a
note that the ISO name is generated from ISO_VERSION (date format), while
leaving the SBOM examples (which use CORTEX_VERSION and default to 0.1.0)
unchanged.

- Updated GitHub Actions workflow to use xz compression for release builds and lz4 for faster CI builds, optimizing build times and output size.
- Modified build.sh to support dynamic compression type based on the build context, improving efficiency and logging.
- Added liblz4-tool to the dependency installation script to support lz4 compression during builds.
Copy link

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 5

🤖 Fix all issues with AI agents
In @.github/workflows/build-iso.yml:
- Around line 245-253: The GitHub Actions step named "Create Release" uses
softprops/action-gh-release@v1 which is outdated; update the uses value to
softprops/action-gh-release@v2 in that step (keep the existing inputs like files
unchanged), commit the workflow change, and validate by running the workflow or
a dry-run to ensure the v2 action accepts the same inputs and produces the
expected release artifacts.
- Around line 192-199: The Verify checksums step's loop can exit silently if no
.sha256 files exist; modify the shell block in the "Verify checksums" step to
detect whether any checksum files were found and validated (e.g., set a counter
or use nullglob to list *.sha256) and fail the step if none were verified by
exiting non-zero or printing an error; ensure you still run sha256sum -c for
each existing "${iso}.sha256" but after the loop check the counter (or absence
of matches) and call exit 1 with a clear message if zero checksum files were
processed.
- Around line 240-243: The checksum commands can fail when no .iso or .deb files
exist; update the workflow step that runs sha256sum and sha512sum to first
detect matching files (e.g., using a glob check or find) and only run sha256sum
*.iso *.deb and sha512sum *.iso *.deb when the file list is non-empty, otherwise
create empty/placeholder SHA256SUMS and SHA512SUMS or skip generation; modify
the shell block containing the sha256sum and sha512sum invocations so it guards
execution with a file-existence check before calling those tools.

In @scripts/build.sh:
- Around line 682-691: The script block that copies DEBIAN control files then
unconditionally runs chmod on "${pkg_dir}/DEBIAN/postinst" and
"${pkg_dir}/DEBIAN/prerm" can fail if those files are absent; update the logic
in scripts/build.sh to test for each file's existence before changing
permissions (or only chmod if the file exists) after the cp step, referencing
the variables PACKAGES_DIR, pkg_name and pkg_dir and the filenames postinst and
prerm so the script skips chmod when a file is missing and avoids a failure.
- Around line 286-294: The arithmetic expansion currently uses `$?` after an
assignment so it captures the exit status of the assignment (always 0) instead
of the function's return value; for each call (validate_provision,
validate_hooks, run_shellcheck) capture the function's exit code into a
temporary variable immediately after the call (e.g., local ret=0;
validate_provision || ret=$?) and then add that ret to errors with
errors=$((errors + ret)); this avoids miscapturing `$?` and is safe if set -e is
enabled.
🧹 Nitpick comments (2)
scripts/install-deps.sh (1)

14-16: Minor inconsistency in log prefix format.

The logging functions here use [+], [!], and [ERROR] prefixes, while scripts/build.sh uses [BUILD], [WARN], and [ERROR]. Consider aligning the prefix style for consistency across scripts. Based on the relevant code snippets from build.sh.

♻️ Optional: Align with build.sh logging style
-log() { echo -e "${GREEN}[+]${NC} $1"; }
-warn() { echo -e "${YELLOW}[!]${NC} $1"; }
+log() { echo -e "${GREEN}[DEPS]${NC} $1"; }
+warn() { echo -e "${YELLOW}[WARN]${NC} $1"; }
scripts/build.sh (1)

547-547: sudo lb build may fail silently under set -e in certain edge cases.

The sudo lb build command runs the actual ISO build. Consider capturing and checking the exit status explicitly, and logging more context on failure to aid debugging.

♻️ Proposed enhancement
 build_iso() {
     header "Building ISO"

     cd "$BUILD_DIR"

     log "Starting live-build (this may take a while)..."
-    sudo lb build
+    if ! sudo lb build; then
+        error "live-build failed. Check build.log for details."
+        exit 1
+    fi

     log "Build complete"
 }
📜 Review details

Configuration used: defaults

Review profile: CHILL

Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between aa34a92 and 070ed6b.

📒 Files selected for processing (3)
  • .github/workflows/build-iso.yml
  • scripts/build.sh
  • scripts/install-deps.sh
🧰 Additional context used
🧬 Code graph analysis (1)
scripts/install-deps.sh (1)
scripts/build.sh (3)
  • error (44-44)
  • log (42-42)
  • warn (43-43)
🪛 actionlint (1.7.10)
.github/workflows/build-iso.yml

246-246: the runner of "softprops/action-gh-release@v1" action is too old to run on GitHub Actions. update the action's version to fix this issue

(action)

⏰ Context from checks skipped due to timeout of 90000ms. You can increase the timeout in your CodeRabbit configuration to a maximum of 15 minutes (900000ms). (2)
  • GitHub Check: Build ISO (amd64)
  • GitHub Check: Build (First Pass)
🔇 Additional comments (8)
scripts/install-deps.sh (2)

32-50: LGTM! Comprehensive package list for live-build.

The package selection covers all necessary build tools including live-build, debootstrap, squashfs-tools, xorriso, bootloader utilities, and the newly added liblz4-tool for lz4 compression support as mentioned in the PR objectives.


56-65: No changes needed. The current bootloader package installation is correct for amd64 builds. The standard grub-pc-bin (64-bit) already supports legacy BIOS systems on amd64 architecture. There is no evidence in the codebase indicating that i386 cross-compilation support or multiarch configuration is required for this project's build process.

.github/workflows/build-iso.yml (2)

33-48: LGTM! Validation job properly configured.

The validation job correctly runs in a Debian bookworm container with necessary dependencies (git, sudo, make, shellcheck) and delegates to the Makefile's validate target.


122-141: Verify privileged Docker mode is acceptable for your CI environment.

The --privileged flag grants extensive capabilities to the container. While this is typically required for live-build operations (loopback mounts, chroot), ensure this aligns with your security policies.

scripts/build.sh (4)

7-7: Good use of strict mode.

set -euo pipefail provides robust error handling: exit on error, undefined variable access, and pipeline failures. This is best practice for build scripts.


63-72: Unquoted glob expansion is intentional but fragile with special characters.

The copy_glob_if_exists function correctly disables shellcheck for the intentional glob expansion. However, if filenames contain spaces or special characters, this will break. Consider using find with -exec for robustness if this becomes an issue.


758-797: LGTM! Comprehensive help documentation.

The help text clearly documents all commands, environment variables, and provides usage examples. This improves developer experience significantly.


803-844: LGTM! Clean entry point with proper command dispatch.

The main function handles command dispatch cleanly with a sensible default (help) and proper error handling for unknown commands.

Comment on lines +192 to +199
- name: Verify checksums
run: |
ls -la
for iso in *.iso; do
if [ -f "${iso}.sha256" ]; then
sha256sum -c "${iso}.sha256"
fi
done
Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Potential issue | 🟡 Minor

Checksum verification loop may silently pass when no checksums exist.

If no .sha256 files are present, the loop completes without error. Consider adding a check to ensure at least one checksum file was verified.

🔧 Proposed fix
      - name: Verify checksums
        run: |
          ls -la
+          verified=0
          for iso in *.iso; do
            if [ -f "${iso}.sha256" ]; then
              sha256sum -c "${iso}.sha256"
+              verified=$((verified + 1))
            fi
          done
+          if [ $verified -eq 0 ]; then
+            echo "WARNING: No checksum files found to verify"
+            exit 1
+          fi
📝 Committable suggestion

‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.

Suggested change
- name: Verify checksums
run: |
ls -la
for iso in *.iso; do
if [ -f "${iso}.sha256" ]; then
sha256sum -c "${iso}.sha256"
fi
done
- name: Verify checksums
run: |
ls -la
verified=0
for iso in *.iso; do
if [ -f "${iso}.sha256" ]; then
sha256sum -c "${iso}.sha256"
verified=$((verified + 1))
fi
done
if [ $verified -eq 0 ]; then
echo "WARNING: No checksum files found to verify"
exit 1
fi
🤖 Prompt for AI Agents
In @.github/workflows/build-iso.yml around lines 192 - 199, The Verify checksums
step's loop can exit silently if no .sha256 files exist; modify the shell block
in the "Verify checksums" step to detect whether any checksum files were found
and validated (e.g., set a counter or use nullglob to list *.sha256) and fail
the step if none were verified by exiting non-zero or printing an error; ensure
you still run sha256sum -c for each existing "${iso}.sha256" but after the loop
check the counter (or absence of matches) and call exit 1 with a clear message
if zero checksum files were processed.

Comment on lines +240 to +243
run: |
cd release
sha256sum *.iso *.deb > SHA256SUMS
sha512sum *.iso *.deb > SHA512SUMS
Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Potential issue | 🟡 Minor

Checksum generation may fail if no matching files exist.

If the build produces no .iso or .deb files, sha256sum and sha512sum will fail with "no input files". Consider adding a guard.

🔧 Proposed fix
      - name: Generate combined checksums
        run: |
          cd release
-          sha256sum *.iso *.deb > SHA256SUMS
-          sha512sum *.iso *.deb > SHA512SUMS
+          if ls *.iso *.deb 1>/dev/null 2>&1; then
+            sha256sum *.iso *.deb > SHA256SUMS
+            sha512sum *.iso *.deb > SHA512SUMS
+          else
+            echo "No ISO or DEB files found for checksumming"
+            exit 1
+          fi
🤖 Prompt for AI Agents
In @.github/workflows/build-iso.yml around lines 240 - 243, The checksum
commands can fail when no .iso or .deb files exist; update the workflow step
that runs sha256sum and sha512sum to first detect matching files (e.g., using a
glob check or find) and only run sha256sum *.iso *.deb and sha512sum *.iso *.deb
when the file list is non-empty, otherwise create empty/placeholder SHA256SUMS
and SHA512SUMS or skip generation; modify the shell block containing the
sha256sum and sha512sum invocations so it guards execution with a file-existence
check before calling those tools.

Comment on lines +286 to +294
;;
provision)
validate_provision || errors=$((errors + $?))
;;
hooks)
validate_hooks || errors=$((errors + $?))
;;
lint)
run_shellcheck || errors=$((errors + $?))
Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Potential issue | 🟠 Major

Arithmetic expansion of $? after || always yields 0 or 1.

When using validate_provision || errors=$((errors + $?)), the $? after || captures the exit status of the assignment (always 0), not validate_provision. The functions return actual error counts, but this pattern doesn't capture them correctly.

🔧 Proposed fix using command substitution or direct capture
        provision)
-            validate_provision || errors=$((errors + $?))
+            validate_provision || errors=1
            ;;
        hooks)
-            validate_hooks || errors=$((errors + $?))
+            validate_hooks || errors=1
            ;;
        lint)
-            run_shellcheck || errors=$((errors + $?))
+            run_shellcheck || errors=1
            ;;
        all)
            validate_preseed
            echo ""
-            validate_provision || errors=$((errors + $?))
+            validate_provision || errors=1
            echo ""
-            validate_hooks || errors=$((errors + $?))
+            validate_hooks || errors=1
            echo ""
-            run_shellcheck || errors=$((errors + $?))
+            run_shellcheck || errors=1
            ;;

Alternatively, if you need the actual error count:

if ! validate_provision; then
    errors=$((errors + $?))
fi

But note that set -e may interfere. A cleaner approach:

local ret=0
validate_provision || ret=$?
errors=$((errors + ret))
📝 Committable suggestion

‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.

Suggested change
;;
provision)
validate_provision || errors=$((errors + $?))
;;
hooks)
validate_hooks || errors=$((errors + $?))
;;
lint)
run_shellcheck || errors=$((errors + $?))
;;
provision)
validate_provision || errors=1
;;
hooks)
validate_hooks || errors=1
;;
lint)
run_shellcheck || errors=1
🤖 Prompt for AI Agents
In @scripts/build.sh around lines 286 - 294, The arithmetic expansion currently
uses `$?` after an assignment so it captures the exit status of the assignment
(always 0) instead of the function's return value; for each call
(validate_provision, validate_hooks, run_shellcheck) capture the function's exit
code into a temporary variable immediately after the call (e.g., local ret=0;
validate_provision || ret=$?) and then add that ret to errors with
errors=$((errors + ret)); this avoids miscapturing `$?` and is safe if set -e is
enabled.

- Changed environment variable from LB_COMPRESSION to SQUASHFS_COMP in the GitHub Actions workflow and build.sh script to ensure uniformity in compression handling.
- This update enhances clarity and maintains consistency across the build process.
Copy link

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 1

🤖 Fix all issues with AI agents
In @.github/workflows/build-iso.yml:
- Around line 245-253: The workflow is using the outdated
softprops/action-gh-release@v1 action which is incompatible with newer runners;
update the uses entry to softprops/action-gh-release@v2 (replace
"softprops/action-gh-release@v1" with "@v2") and ensure the existing inputs (the
multi-line files list: release/*.iso, release/*.deb, release/SHA256SUMS,
release/SHA512SUMS, release/sbom/*) remain unchanged and still supported by v2;
run a test pipeline to confirm the release step executes successfully and adjust
any v2-specific input names if the action's docs require it.
🧹 Nitpick comments (3)
.github/workflows/build-iso.yml (1)

192-208: Add nullglob or check for ISO existence to handle missing files gracefully.

If no .iso files exist (e.g., build failure), *.iso remains literal and the loops will fail confusingly. Consider adding a check or using bash's nullglob.

Suggested improvement
       - name: Verify checksums
         run: |
           ls -la
+          shopt -s nullglob
+          isos=(*.iso)
+          if [ ${#isos[@]} -eq 0 ]; then
+            echo "ERROR: No ISO files found"
+            exit 1
+          fi
           for iso in *.iso; do
             if [ -f "${iso}.sha256" ]; then
               sha256sum -c "${iso}.sha256"
             fi
           done
scripts/build.sh (2)

541-550: Consider validating sudo availability before use.

The script uses sudo lb build without first checking if sudo is available or if the user has the necessary privileges. This could lead to confusing failures partway through the build process.

💡 Suggested improvement

Add a sudo check early in cmd_build:

 cmd_build() {
     header "Building Cortex Linux ISO"
     log "Architecture: ${ARCH}"
     log "Debian version: ${DEBIAN_VERSION}"
 
+    # Verify sudo access early to fail fast
+    if ! sudo -v 2>/dev/null; then
+        error "sudo access required for building ISO"
+        exit 1
+    fi
+
     prepare_build_dir

619-626: Add safety check for rm -rf on variable paths.

While set -u provides some protection against unset variables, adding an explicit check before destructive operations is a defensive practice that prevents accidental data loss if variable definitions are ever modified.

💡 Suggested defensive check
 cmd_clean_all() {
     header "Cleaning all build artifacts"
 
+    # Safety check to prevent accidental rm -rf on root or home
+    if [[ -z "$BUILD_DIR" || "$BUILD_DIR" == "/" || "$BUILD_DIR" == "$HOME" ]]; then
+        error "BUILD_DIR has unsafe value: '$BUILD_DIR'"
+        exit 1
+    fi
+    if [[ -z "$OUTPUT_DIR" || "$OUTPUT_DIR" == "/" || "$OUTPUT_DIR" == "$HOME" ]]; then
+        error "OUTPUT_DIR has unsafe value: '$OUTPUT_DIR'"
+        exit 1
+    fi
+
     rm -rf "$BUILD_DIR"
     rm -rf "$OUTPUT_DIR"
 
     log "Full clean complete"
 }
📜 Review details

Configuration used: defaults

Review profile: CHILL

Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between 070ed6b and 26cc7f5.

📒 Files selected for processing (2)
  • .github/workflows/build-iso.yml
  • scripts/build.sh
🧰 Additional context used
🪛 actionlint (1.7.10)
.github/workflows/build-iso.yml

246-246: the runner of "softprops/action-gh-release@v1" action is too old to run on GitHub Actions. update the action's version to fix this issue

(action)

⏰ Context from checks skipped due to timeout of 90000ms. You can increase the timeout in your CodeRabbit configuration to a maximum of 15 minutes (900000ms). (2)
  • GitHub Check: Build ISO (amd64)
  • GitHub Check: Build (First Pass)
🔇 Additional comments (16)
.github/workflows/build-iso.yml (4)

75-86: LGTM on the matrix strategy and job dependencies.

The multi-architecture build matrix with fail-fast: false is appropriate - it allows both architectures to complete even if one fails, which is useful for debugging. The dependency chain (validatebuild-branding-packagebuild-iso) ensures proper build ordering.


122-141: Docker-based build approach is solid.

The compression strategy (xz for releases, lz4 for PR builds) provides a good balance between size and build speed. Using set -e ensures the build fails fast on errors. The privileged container is necessary for live-build operations.


210-237: Release artifact collection is well-structured.

The continue-on-error: true for SBOM download is appropriate since SBOM generation may be optional. Using merge-multiple: true with patterns cleanly consolidates artifacts from the matrix builds.


88-97: The jlumbroso/free-disk-space action does not publish versioned releases. The repository is maintained only on the main branch, making @main the only available reference. The suggested pin to v1.3.1 is not a valid version tag for this action and cannot be implemented.

If reproducibility and supply-chain safety are concerns with using @main, consider evaluating alternative disk-cleanup actions that publish releases, or accept the trade-off of using @main for this particular action.

Likely an incorrect or invalid review comment.

scripts/build.sh (12)

1-36: LGTM!

The script header and configuration section are well-structured with sensible defaults, proper set -euo pipefail for strict error handling, and clear organization.


42-47: LGTM!

Clean logging functions with appropriate color coding and stderr output for errors.


63-72: LGTM!

The copy_glob_if_exists function correctly uses shellcheck disable for intentional glob expansion. The pattern check before copy prevents errors on non-matching globs.


90-150: LGTM!

Thorough dependency checking with clear separation between required and optional dependencies. The version checks using dpkg --compare-versions are appropriate for a Debian-based distro build script.


156-180: LGTM!

The preseed validation correctly checks for trailing whitespace which could cause issues in preseed files. The regex '^[^#]*[[:space:]]$' appropriately ignores commented lines.


277-318: LGTM!

The cmd_validate dispatcher is well-structured with clear mode handling. The error accumulation pattern || errors=$((errors + $?)) correctly captures function return values before the || short-circuit evaluates.


324-420: LGTM!

Comprehensive test suite covering preseed files, provisioning scripts, branding assets, hooks, and package control files. The syntax validation for shell scripts and consistent pass/fail reporting provide good pre-build verification.


491-518: LGTM!

The configure_live_build function has a well-documented compression choice (lz4 for CI speed vs xz for release size), comprehensive lb config options, and good logging.


644-658: LGTM!

The sync function correctly checks for directory existence before copying and provides appropriate warnings.


664-752: LGTM!

The branding package function correctly structures the .deb package with appropriate directory hierarchy, sets proper permissions on maintainer scripts, and includes the GRUB background conversion for compatibility. The cleanup of the temporary package directory prevents accumulation of build artifacts.


758-797: LGTM!

Comprehensive help documentation with clear command descriptions, environment variable documentation, and practical examples.


803-844: LGTM!

Clean command dispatcher with sensible default to help, graceful handling of missing arguments via shift || true, and proper error handling for unknown commands.

Comment on lines 245 to +253
- name: Create Release
uses: softprops/action-gh-release@v1
with:
files: |
*.iso
*.deb
SHA256SUMS
SHA512SUMS
release/*.iso
release/*.deb
release/SHA256SUMS
release/SHA512SUMS
release/sbom/*
Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Potential issue | 🟡 Minor

Update softprops/action-gh-release to v2.

Static analysis indicates @v1 is too old for current GitHub Actions runners. The action should be updated to v2 for compatibility and continued support.

Suggested fix
       - name: Create Release
-        uses: softprops/action-gh-release@v1
+        uses: softprops/action-gh-release@v2
         with:
           files: |
📝 Committable suggestion

‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.

Suggested change
- name: Create Release
uses: softprops/action-gh-release@v1
with:
files: |
*.iso
*.deb
SHA256SUMS
SHA512SUMS
release/*.iso
release/*.deb
release/SHA256SUMS
release/SHA512SUMS
release/sbom/*
- name: Create Release
uses: softprops/action-gh-release@v2
with:
files: |
release/*.iso
release/*.deb
release/SHA256SUMS
release/SHA512SUMS
release/sbom/*
🧰 Tools
🪛 actionlint (1.7.10)

246-246: the runner of "softprops/action-gh-release@v1" action is too old to run on GitHub Actions. update the action's version to fix this issue

(action)

🤖 Prompt for AI Agents
In @.github/workflows/build-iso.yml around lines 245 - 253, The workflow is
using the outdated softprops/action-gh-release@v1 action which is incompatible
with newer runners; update the uses entry to softprops/action-gh-release@v2
(replace "softprops/action-gh-release@v1" with "@v2") and ensure the existing
inputs (the multi-line files list: release/*.iso, release/*.deb,
release/SHA256SUMS, release/SHA512SUMS, release/sbom/*) remain unchanged and
still supported by v2; run a test pipeline to confirm the release step executes
successfully and adjust any v2-specific input names if the action's docs require
it.

- Implemented functionality to copy the GRUB theme from the branding directory to the includes.chroot directory during the build process.
- Ensured that the theme is copied both in the prepare_build_dir and cmd_sync functions, enhancing the customization of the installed system's boot experience.
Copy link

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 1

🤖 Fix all issues with AI agents
In @scripts/build.sh:
- Around line 695-704: The script unconditionally chmods DEBIAN/postinst and
DEBIAN/prerm which will fail if those files are absent; update the block after
the cp to test each file exists before changing mode (e.g., if [ -f
"${pkg_dir}/DEBIAN/postinst" ]; then chmod 755 "${pkg_dir}/DEBIAN/postinst"; fi
and same for prerm), or alternatively iterate over existing files matching
"${pkg_dir}/DEBIAN/postinst" and "${pkg_dir}/DEBIAN/prerm" and chmod only those
present to avoid "No such file or directory" errors.
🧹 Nitpick comments (2)
scripts/build.sh (2)

375-387: Minor inconsistency: Hook test only checks .hook.chroot files.

The cmd_test function only tests *.hook.chroot files, while validate_hooks (line 217) tests both *.hook.chroot and *.hook.binary. Consider aligning the test coverage.

♻️ Suggested fix
     # Test hooks
     log "Testing live-build hooks..."
     local hooks_dir="${ISO_DIR}/live-build/config/hooks/live"
-    for hook in "$hooks_dir"/*.hook.chroot; do
+    for hook in "$hooks_dir"/*.hook.chroot "$hooks_dir"/*.hook.binary; do
         if [ -f "$hook" ]; then
             if bash -n "$hook"; then
                 pass "$(basename "$hook"): syntax OK"

652-670: Silent failure on copy errors.

The [ -d ... ] && cp -r ... pattern masks copy failures. If a directory exists but the copy fails (e.g., permission denied, disk full), no error is reported and the function logs "Config synced" despite incomplete sync.

♻️ Suggested fix using explicit error handling
 cmd_sync() {
     header "Syncing config"

     if [ -d "$BUILD_DIR" ]; then
-        [ -d "${ISO_DIR}/live-build/config/hooks" ] && \
-            cp -r "${ISO_DIR}/live-build/config/hooks" "${BUILD_DIR}/config/"
-        [ -d "${ISO_DIR}/live-build/config/includes.chroot" ] && \
-            cp -r "${ISO_DIR}/live-build/config/includes.chroot" "${BUILD_DIR}/config/"
-        [ -d "${ISO_DIR}/live-build/config/includes.binary" ] && \
-            cp -r "${ISO_DIR}/live-build/config/includes.binary" "${BUILD_DIR}/config/"
+        if [ -d "${ISO_DIR}/live-build/config/hooks" ]; then
+            cp -r "${ISO_DIR}/live-build/config/hooks" "${BUILD_DIR}/config/" && log "Synced hooks"
+        fi
+        if [ -d "${ISO_DIR}/live-build/config/includes.chroot" ]; then
+            cp -r "${ISO_DIR}/live-build/config/includes.chroot" "${BUILD_DIR}/config/" && log "Synced includes.chroot"
+        fi
+        if [ -d "${ISO_DIR}/live-build/config/includes.binary" ]; then
+            cp -r "${ISO_DIR}/live-build/config/includes.binary" "${BUILD_DIR}/config/" && log "Synced includes.binary"
+        fi
         # Copy GRUB theme to includes.chroot
         if [ -d "${BRANDING_DIR}/grub/cortex" ]; then
             mkdir -p "${BUILD_DIR}/config/includes.chroot/boot/grub/themes/cortex"
-            cp -r "${BRANDING_DIR}/grub/cortex/"* "${BUILD_DIR}/config/includes.chroot/boot/grub/themes/cortex/"
+            cp -r "${BRANDING_DIR}/grub/cortex/"* "${BUILD_DIR}/config/includes.chroot/boot/grub/themes/cortex/" && log "Synced GRUB theme"
         fi
         log "Config synced"
     else
         warn "Build directory not found: ${BUILD_DIR}"
     fi
 }
📜 Review details

Configuration used: defaults

Review profile: CHILL

Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between 26cc7f5 and c9c502b.

📒 Files selected for processing (1)
  • scripts/build.sh
⏰ Context from checks skipped due to timeout of 90000ms. You can increase the timeout in your CodeRabbit configuration to a maximum of 15 minutes (900000ms). (2)
  • GitHub Check: Build ISO (amd64)
  • GitHub Check: Build (First Pass)
🔇 Additional comments (8)
scripts/build.sh (8)

1-48: LGTM!

The configuration and logging setup is well-structured. Good use of set -euo pipefail for strict error handling, and the path derivation from SCRIPT_DIR is safe.


53-84: LGTM!

Helper functions are appropriately designed. The intentional unquoted glob expansion in copy_glob_if_exists is acceptable for this controlled build environment, and the shellcheck disable comment correctly acknowledges this.


90-150: LGTM!

Thorough dependency checking with good separation between required and optional dependencies. The version checking for live-build and Python is well implemented with appropriate fallbacks and warnings.


152-318: LGTM!

The validation subsystem is well-designed with clear separation of concerns. The || errors=$((errors + $?)) pattern correctly captures error counts while preventing script exit under set -e.


422-609: LGTM!

The ISO build functions are well-structured. The dual GRUB theme copying (in prepare_build_dir for the installed system and copy_grub_theme for live boot) aligns with the commit message objectives. The exit 1 on missing required theme.txt is appropriate.


611-646: LGTM!

Clean functions are appropriately designed. The rm -rf operations on build/output directories are safe since the paths are derived from the project root.


771-810: LGTM!

Comprehensive and well-organized help text covering all commands, environment variables, and examples.


816-857: LGTM!

Clean dispatcher pattern with good defaults. The shift || true pattern correctly handles the case when no arguments are provided.

Comment on lines +695 to +704
# Copy DEBIAN control files
log "Copying DEBIAN control files..."
if [ -d "${PACKAGES_DIR}/${pkg_name}/DEBIAN" ]; then
cp "${PACKAGES_DIR}/${pkg_name}/DEBIAN/"* "${pkg_dir}/DEBIAN/"
chmod 755 "${pkg_dir}/DEBIAN/postinst"
chmod 755 "${pkg_dir}/DEBIAN/prerm"
else
error "DEBIAN control files not found"
exit 1
fi
Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Potential issue | 🟡 Minor

Potential error if postinst or prerm don't exist.

Lines 699-700 unconditionally run chmod 755 on postinst and prerm, but the glob copy on line 698 doesn't guarantee these specific files exist. If either file is missing from the source DEBIAN/ directory, the script will fail with "No such file or directory".

🐛 Suggested fix
     # Copy DEBIAN control files
     log "Copying DEBIAN control files..."
     if [ -d "${PACKAGES_DIR}/${pkg_name}/DEBIAN" ]; then
         cp "${PACKAGES_DIR}/${pkg_name}/DEBIAN/"* "${pkg_dir}/DEBIAN/"
-        chmod 755 "${pkg_dir}/DEBIAN/postinst"
-        chmod 755 "${pkg_dir}/DEBIAN/prerm"
+        [ -f "${pkg_dir}/DEBIAN/postinst" ] && chmod 755 "${pkg_dir}/DEBIAN/postinst"
+        [ -f "${pkg_dir}/DEBIAN/prerm" ] && chmod 755 "${pkg_dir}/DEBIAN/prerm"
     else
         error "DEBIAN control files not found"
         exit 1
     fi
🤖 Prompt for AI Agents
In @scripts/build.sh around lines 695 - 704, The script unconditionally chmods
DEBIAN/postinst and DEBIAN/prerm which will fail if those files are absent;
update the block after the cp to test each file exists before changing mode
(e.g., if [ -f "${pkg_dir}/DEBIAN/postinst" ]; then chmod 755
"${pkg_dir}/DEBIAN/postinst"; fi and same for prerm), or alternatively iterate
over existing files matching "${pkg_dir}/DEBIAN/postinst" and
"${pkg_dir}/DEBIAN/prerm" and chmod only those present to avoid "No such file or
directory" errors.

- Deleted the .github/workflows/reproducible-builds.yml file, removing the entire reproducible builds workflow from the CI/CD pipeline.
- This change simplifies the workflow configuration and eliminates the need for maintaining the associated build and comparison steps.
Copy link

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 2

🤖 Fix all issues with AI agents
In @iso/live-build/config/hooks/live/50-cortex-branding.hook.chroot:
- Around line 99-102: The sed replacement will never trigger the echo fallback
because sed exits 0 when no match is found; change the logic to explicitly test
for an existing GRUB_DISTRIBUTOR line first (e.g., grep -q '^GRUB_DISTRIBUTOR='
/etc/default/grub), and if found run the sed substitution to replace its value,
otherwise append GRUB_DISTRIBUTOR="Cortex Linux" to /etc/default/grub; update
the block that currently runs sed ... || echo to use this conditional check so
the echo fallback actually occurs when the variable is missing.
- Around line 19-31: The branding assets use two different domains; standardize
to a single canonical domain across all files by replacing all occurrences of
the alternate domain with the chosen canonical one: update the os-release
entries HOME_URL, SUPPORT_URL and PRIVACY_POLICY_URL (currently set to
cortex.io) to match the canonical domain you pick, and update the MOTD footer
(the footer text that references cortexlinux.com/support and
docs.cortexlinux.com) and any branding comments that reference
apt.cortexlinux.com so they all use the same domain string consistently; ensure
you change every occurrence (HOME_URL, SUPPORT_URL, PRIVACY_POLICY_URL, MOTD
footer text, and branding comments) to the chosen canonical domain.
🧹 Nitpick comments (5)
iso/live-build/config/hooks/live/50-cortex-branding.hook.chroot (5)

160-162: Loop may silently skip on glob mismatch.

When /etc/update-motd.d/* matches no files, bash defaults to iterating over the literal string. The [ -f "$f" ] guard handles this, but adding shopt -s nullglob or checking for directory existence first would be cleaner.

♻️ Optional improvement
 # Disable default Debian MOTD scripts
+if [ -d /etc/update-motd.d ]; then
-for f in /etc/update-motd.d/*; do
-    [ -f "$f" ] && chmod -x "$f"
-done
+    for f in /etc/update-motd.d/*; do
+        [ -f "$f" ] && chmod -x "$f"
+    done
+fi

198-206: Minor: Avoid useless use of cat.

cat /proc/loadavg | awk can be simplified to awk '{print $1, $2, $3}' /proc/loadavg.

♻️ Suggested simplification
-LOAD=$(cat /proc/loadavg | awk '{print $1, $2, $3}')
+LOAD=$(awk '{print $1, $2, $3}' /proc/loadavg)

232-245: Potential slow login if apt cache is stale.

apt list --upgradable can be slow without a recent apt update. Consider caching results or adding a timeout to avoid blocking user login.

♻️ Add timeout to prevent login delays
     else
         # Quick check (may not be accurate without apt update)
-        UPDATES=$(apt list --upgradable 2>/dev/null | grep -c upgradable || echo "0")
+        UPDATES=$(timeout 2 apt list --upgradable 2>/dev/null | grep -c upgradable || echo "0")
         SECURITY="?"
     fi

269-274: Potential command injection if notice file contains shell metacharacters.

While /opt/cortex/notice is likely controlled, piping file contents directly to sed and displaying could have unexpected behavior with special characters. Consider using cat with proper quoting or printf '%s\n'.

♻️ Safer approach
 CORTEX_NOTICE="/opt/cortex/notice"
 if [ -f "$CORTEX_NOTICE" ]; then
     echo -e "  ${CYAN}◆ Cortex Notice:${RESET}"
-    cat "$CORTEX_NOTICE" | sed 's/^/    /'
+    while IFS= read -r line; do
+        printf '    %s\n' "$line"
+    done < "$CORTEX_NOTICE"
     echo ""
 fi

352-362: Consider using image_source instead of ascii_distro for custom ASCII.

Neofetch's ascii_distro is designed for built-in distro names. For custom ASCII art, using image_source="/usr/share/neofetch/ascii/distro/cortex" in the config may be more reliable.

♻️ More reliable custom ASCII configuration
 # Cortex branding colors (purple/cyan)
 colors=(5 6 7 5 6 7)
 bold="on"
-ascii_distro="cortex"
+image_source="/usr/share/neofetch/ascii/distro/cortex"
+image_backend="ascii"
📜 Review details

Configuration used: defaults

Review profile: CHILL

Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between c9c502b and 09eede1.

📒 Files selected for processing (3)
  • .github/workflows/reproducible-builds.yml
  • iso/live-build/config/hooks/live/50-cortex-branding.hook.chroot
  • iso/live-build/config/package-lists/cortex.list.chroot
💤 Files with no reviewable changes (1)
  • .github/workflows/reproducible-builds.yml
⏰ Context from checks skipped due to timeout of 90000ms. You can increase the timeout in your CodeRabbit configuration to a maximum of 15 minutes (900000ms). (1)
  • GitHub Check: Build ISO (amd64)
🔇 Additional comments (7)
iso/live-build/config/package-lists/cortex.list.chroot (4)

1-14: LGTM! System base packages are well-organized.

The essential system utilities section correctly includes the minimal required packages for a functioning Debian-based system (systemd, dbus, udev, init).


191-191: Good addition to support branding.

Adding neofetch aligns with the branding hook script that creates custom Cortex neofetch configuration and ASCII art.


234-239: Placeholder packages are clearly documented.

The commented Cortex packages with the explanatory note about APT repository availability is a reasonable approach for bootstrapping.


151-156: No action needed — nvidia-smi is available as a standalone package in Debian Bookworm.

nvidia-smi is provided as a standalone non-free package in Debian Bookworm and resolves independently with its dependencies (libnvidia-ml runtime and related packages). The package list inclusion is valid.

Likely an incorrect or invalid review comment.

iso/live-build/config/hooks/live/50-cortex-branding.hook.chroot (3)

9-11: Good use of strict error handling.

set -e ensures the script fails fast on errors, appropriate for a build-time hook where partial branding would be problematic.


368-373: Good: initramfs update included for Plymouth changes.

Running update-initramfs -u ensures the Plymouth theme is properly included in the initramfs for boot splash display.


345-349: No action required. The ascii_distro="cortex" configuration will work correctly because the hook script itself creates the ASCII file at /usr/share/neofetch/ascii/distro/cortex (lines 352–354) before the neofetch config references it. The ASCII art is properly formatted with valid color tokens.

Likely an incorrect or invalid review comment.

Comment on lines +19 to +31
cat > /etc/os-release << 'EOF'
PRETTY_NAME="Cortex Linux"
NAME="Cortex Linux"
VERSION_ID="1.0"
VERSION="1.0 (Bookworm)"
VERSION_CODENAME=bookworm
ID=cortex
ID_LIKE=debian
HOME_URL="https://cortex.io"
SUPPORT_URL="https://cortex.io/support"
BUG_REPORT_URL="https://github.com/cortex/cortex-distro/issues"
PRIVACY_POLICY_URL="https://cortex.io/privacy"
EOF
Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Potential issue | 🟡 Minor

🧩 Analysis chain

🏁 Script executed:

#!/bin/bash
# Search for domain references in the entire repository
echo "=== All references to cortex domains ==="
rg "cortex\.io|cortexlinux\.com|cortexlinux\.io|docs\.cortexlinux" -n --type sh --type txt
echo ""
echo "=== Checking the specific file ==="
cat -n iso/live-build/config/hooks/live/50-cortex-branding.hook.chroot 2>/dev/null | head -40

Repository: cortexlinux/cortex-distro

Length of output: 2331


🏁 Script executed:

#!/bin/bash
# Check file length and search for MOTD
echo "=== File line count ==="
wc -l iso/live-build/config/hooks/live/50-cortex-branding.hook.chroot

echo ""
echo "=== Looking for MOTD section ==="
rg -n "MOTD|motd" iso/live-build/config/hooks/live/50-cortex-branding.hook.chroot

echo ""
echo "=== Search for domain references in the branding file ==="
rg -n "cortex\.io|cortexlinux\.com|cortexlinux\.io|docs\.cortexlinux" iso/live-build/config/hooks/live/50-cortex-branding.hook.chroot

Repository: cortexlinux/cortex-distro

Length of output: 1566


Resolve domain inconsistency across branding files.

The os-release file uses cortex.io (HOME_URL, SUPPORT_URL, PRIVACY_POLICY_URL) while the MOTD footer references cortexlinux.com and docs.cortexlinux.com. Choose one canonical domain and update all branding files accordingly:

  • os-release (lines 27-30): cortex.io
  • MOTD footer (line 306): cortexlinux.com/support and docs.cortexlinux.com
  • Branding file comments (lines 53, 56-57): reference apt.cortexlinux.com
🤖 Prompt for AI Agents
In @iso/live-build/config/hooks/live/50-cortex-branding.hook.chroot around lines
19 - 31, The branding assets use two different domains; standardize to a single
canonical domain across all files by replacing all occurrences of the alternate
domain with the chosen canonical one: update the os-release entries HOME_URL,
SUPPORT_URL and PRIVACY_POLICY_URL (currently set to cortex.io) to match the
canonical domain you pick, and update the MOTD footer (the footer text that
references cortexlinux.com/support and docs.cortexlinux.com) and any branding
comments that reference apt.cortexlinux.com so they all use the same domain
string consistently; ensure you change every occurrence (HOME_URL, SUPPORT_URL,
PRIVACY_POLICY_URL, MOTD footer text, and branding comments) to the chosen
canonical domain.

Comment on lines +99 to +102
# Also set timeout and other branding options
sed -i 's|^GRUB_DISTRIBUTOR=.*|GRUB_DISTRIBUTOR="Cortex Linux"|' /etc/default/grub || \
echo 'GRUB_DISTRIBUTOR="Cortex Linux"' >> /etc/default/grub
fi
Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Potential issue | 🔴 Critical

Bug: GRUB_DISTRIBUTOR will not be appended if missing.

The sed command returns exit code 0 even when the pattern isn't found (it just produces no substitutions). The || echo fallback will never execute.

🐛 Proposed fix
     # Also set timeout and other branding options
-    sed -i 's|^GRUB_DISTRIBUTOR=.*|GRUB_DISTRIBUTOR="Cortex Linux"|' /etc/default/grub || \
-        echo 'GRUB_DISTRIBUTOR="Cortex Linux"' >> /etc/default/grub
+    if grep -q "^GRUB_DISTRIBUTOR=" /etc/default/grub; then
+        sed -i 's|^GRUB_DISTRIBUTOR=.*|GRUB_DISTRIBUTOR="Cortex Linux"|' /etc/default/grub
+    else
+        echo 'GRUB_DISTRIBUTOR="Cortex Linux"' >> /etc/default/grub
+    fi
📝 Committable suggestion

‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.

Suggested change
# Also set timeout and other branding options
sed -i 's|^GRUB_DISTRIBUTOR=.*|GRUB_DISTRIBUTOR="Cortex Linux"|' /etc/default/grub || \
echo 'GRUB_DISTRIBUTOR="Cortex Linux"' >> /etc/default/grub
fi
# Also set timeout and other branding options
if grep -q "^GRUB_DISTRIBUTOR=" /etc/default/grub; then
sed -i 's|^GRUB_DISTRIBUTOR=.*|GRUB_DISTRIBUTOR="Cortex Linux"|' /etc/default/grub
else
echo 'GRUB_DISTRIBUTOR="Cortex Linux"' >> /etc/default/grub
fi
fi
🤖 Prompt for AI Agents
In @iso/live-build/config/hooks/live/50-cortex-branding.hook.chroot around lines
99 - 102, The sed replacement will never trigger the echo fallback because sed
exits 0 when no match is found; change the logic to explicitly test for an
existing GRUB_DISTRIBUTOR line first (e.g., grep -q '^GRUB_DISTRIBUTOR='
/etc/default/grub), and if found run the sed substitution to replace its value,
otherwise append GRUB_DISTRIBUTOR="Cortex Linux" to /etc/default/grub; update
the block that currently runs sed ... || echo to use this conditional check so
the echo fallback actually occurs when the variable is missing.

- Introduced a new GNOME terminal profile for Cortex Linux, enhancing user experience with customized colors and settings.
- Updated PAM configuration files for login and SSH services to enable dynamic message of the day (MOTD) display.
- Added a new .bashrc file with Cortex-specific aliases and prompt customization for improved usability and branding.
Copy link

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 2

🤖 Fix all issues with AI agents
In @iso/live-build/config/hooks/live/99-configure-gnome.hook.chroot:
- Around line 98-124: The GNOME Terminal profile block uses an invalid literal
name "cortex"; replace the profile path
[org/gnome/terminal/legacy/profiles:/:cortex] with a valid UUID-based path
(e.g., [org/gnome/terminal/legacy/profiles:/:<UUID>]) and update
default='cortex' and list=['cortex'] to reference that same UUID string; keep
visible-name='Cortex' unchanged and generate the UUID with uuidgen (or similar)
so all three places (profile section name, default, and list) use the identical
UUID.

In @iso/live-build/config/includes.chroot/etc/pam.d/login:
- Line 16: Remove the deprecated and insecure user_readenv=1 option from the
pam_env.so session line: locate the line that invokes pam_env.so (currently
"session required pam_env.so user_readenv=1 envfile=/etc/default/locale") and
either drop the user_readenv=1 entirely or change it to user_readenv=0 so the
entry reads "session required pam_env.so envfile=/etc/default/locale" (or with
user_readenv=0) to mitigate the CVE issues.
🧹 Nitpick comments (9)
iso/live-build/config/includes.chroot/etc/skel/.bashrc (2)

46-61: Consider using bash pattern matching for efficiency.

The function spawns multiple subshells (echo | grep) for each status check. Using bash's built-in pattern matching would be faster, especially since this runs on every prompt.

♻️ Suggested refactor using bash patterns
 __git_status() {
     local status=""
     local git_status=$(git status --porcelain 2>/dev/null)
     if [[ -n "$git_status" ]]; then
-        if echo "$git_status" | grep -q "^??"; then
+        if [[ "$git_status" == *$'\n?? '* ]] || [[ "$git_status" == '?? '* ]]; then
             status+="?"
         fi
-        if echo "$git_status" | grep -q "^.M\|^M"; then
+        if [[ "$git_status" =~ (^|$'\n')[^\ ]?M ]]; then
             status+="*"
         fi
-        if echo "$git_status" | grep -q "^A\|^.A"; then
+        if [[ "$git_status" =~ (^|$'\n')[^\ ]?A ]]; then
             status+="+"
         fi
     fi
     [[ -n "$status" ]] && echo " $status"
 }

87-89: Remove redundant stderr redirection.

&>/dev/null already redirects both stdout and stderr, making the trailing 2>&1 redundant.

♻️ Suggested fix
-    if git rev-parse --is-inside-work-tree &>/dev/null 2>&1; then
+    if git rev-parse --is-inside-work-tree &>/dev/null; then
iso/live-build/config/hooks/live/99-configure-gnome.hook.chroot (3)

25-40: Duplicate GDM configuration files with identical content.

Both daemon.conf (Lines 27-32) and custom.conf (Lines 35-40) contain the same [daemon] section. GDM3 typically reads from custom.conf, making daemon.conf redundant. This duplication may cause maintenance confusion.

Consider removing daemon.conf and keeping only custom.conf:

Proposed fix
 # Configure GDM to start automatically in live mode
 mkdir -p /etc/gdm3
-cat > /etc/gdm3/daemon.conf << 'EOF'
-# Cortex Linux GDM Configuration for Live Environment
-[daemon]
-AutomaticLoginEnable=true
-AutomaticLogin=cortex
-EOF
-
-# Configure GDM session
 cat > /etc/gdm3/custom.conf << 'EOF'
 # Cortex Linux GDM Custom Configuration
 [daemon]
 AutomaticLoginEnable=true
 AutomaticLogin=cortex
+
+[security]
+
+[xdmcp]
+
+[chooser]
+
+[debug]
 EOF

61-61: Unnecessary executable permission on .desktop file.

Desktop entry files don't require executable permission in modern GNOME environments. The Exec line is interpreted by the desktop environment, not executed directly.

Proposed fix
-chmod +x /etc/skel/.config/autostart/welcome.desktop

150-153: Schema compilation may fail silently in chroot.

The glib-compile-schemas call suppresses all output, making debugging difficult if it fails. Consider adding error handling or at least logging the result.

Proposed fix
 # Compile schemas
 if command -v glib-compile-schemas &>/dev/null; then
-    glib-compile-schemas /usr/share/glib-2.0/schemas/
+    glib-compile-schemas /usr/share/glib-2.0/schemas/ || echo "    WARNING: Failed to compile GLib schemas"
 fi
iso/live-build/config/hooks/live/50-cortex-branding.hook.chroot (4)

99-102: Potential double-append of GRUB_DISTRIBUTOR.

If sed fails for a reason other than the pattern not being found (e.g., permission issue), the || echo fallback will append a duplicate GRUB_DISTRIBUTOR line. Consider checking if the key exists first:

Proposed fix
     # Also set timeout and other branding options
-    sed -i 's|^GRUB_DISTRIBUTOR=.*|GRUB_DISTRIBUTOR="Cortex Linux"|' /etc/default/grub || \
-        echo 'GRUB_DISTRIBUTOR="Cortex Linux"' >> /etc/default/grub
+    if grep -q "^GRUB_DISTRIBUTOR=" /etc/default/grub; then
+        sed -i 's|^GRUB_DISTRIBUTOR=.*|GRUB_DISTRIBUTOR="Cortex Linux"|' /etc/default/grub
+    else
+        echo 'GRUB_DISTRIBUTOR="Cortex Linux"' >> /etc/default/grub
+    fi
 fi

232-245: Update check logic conflates errors with "no updates".

Line 239's grep -c upgradable || echo "0" returns "0" both when there are no updates and when apt list fails. This could mislead users into thinking their system is up-to-date when apt encountered an error.

Proposed fix
     else
         # Quick check (may not be accurate without apt update)
-        UPDATES=$(apt list --upgradable 2>/dev/null | grep -c upgradable || echo "0")
+        UPDATES=$(apt list --upgradable 2>/dev/null | grep -c upgradable) || UPDATES="?"
         SECURITY="?"
     fi

260-263: Complex pipeline may produce unexpected output with special characters.

If package names contain unusual characters, the tr '\n' ',' and sed pipeline could produce malformed output. Additionally, cat | sed is a UUOC (useless use of cat).

Proposed fix
     if [ -f "/var/run/reboot-required.pkgs" ]; then
-        REBOOT_PKGS=$(cat /var/run/reboot-required.pkgs | head -3 | tr '\n' ', ' | sed 's/,$//')
+        REBOOT_PKGS=$(head -3 /var/run/reboot-required.pkgs | paste -sd ', ')
         echo -e "  ${GRAY}  Packages: ${REBOOT_PKGS}${RESET}"
     fi

269-274: UUOC and potential issues with untrusted notice content.

Using cat "$CORTEX_NOTICE" | sed is unnecessary, and if the notice file contains escape sequences, they could be interpreted unexpectedly by echo -e elsewhere or cause display issues.

Proposed fix
 CORTEX_NOTICE="/opt/cortex/notice"
 if [ -f "$CORTEX_NOTICE" ]; then
     echo -e "  ${CYAN}◆ Cortex Notice:${RESET}"
-    cat "$CORTEX_NOTICE" | sed 's/^/    /'
+    sed 's/^/    /' "$CORTEX_NOTICE"
     echo ""
 fi
📜 Review details

Configuration used: defaults

Review profile: CHILL

Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between 09eede1 and 6564a1a.

📒 Files selected for processing (6)
  • branding/terminal/cortex-terminal.dconf
  • iso/live-build/config/hooks/live/50-cortex-branding.hook.chroot
  • iso/live-build/config/hooks/live/99-configure-gnome.hook.chroot
  • iso/live-build/config/includes.chroot/etc/pam.d/login
  • iso/live-build/config/includes.chroot/etc/pam.d/sshd
  • iso/live-build/config/includes.chroot/etc/skel/.bashrc
✅ Files skipped from review due to trivial changes (1)
  • branding/terminal/cortex-terminal.dconf
⏰ Context from checks skipped due to timeout of 90000ms. You can increase the timeout in your CodeRabbit configuration to a maximum of 15 minutes (900000ms). (1)
  • GitHub Check: Build ISO (amd64)
🔇 Additional comments (8)
iso/live-build/config/includes.chroot/etc/skel/.bashrc (5)

1-8: LGTM - Well-structured shell configuration.

The interactive shell check, history configuration, shell options, dircolors setup, and bash-completion sourcing all follow standard Debian/Ubuntu conventions and are implemented correctly.

Also applies to: 10-24, 103-114, 146-155


26-43: LGTM - Thoughtful prompt design.

The branded prompt correctly captures exit code at function entry, differentiates root vs. regular user styling, and includes git branch/status integration. The use of PROMPT_COMMAND for dynamic prompt generation is appropriate.

Also applies to: 64-86, 90-101


119-144: LGTM - Reasonable alias set.

The safety aliases (rm -i, cp -i, mv -i) are user-friendly defaults, and the htop-to-top fallback (line 139) gracefully handles missing packages. Cortex-specific aliases provide convenient shortcuts for system maintenance.


157-164: LGTM - Smart neofetch gate.

Using CORTEX_WELCOMED environment variable to prevent duplicate neofetch runs in subshells is a good pattern. The command -v check ensures graceful handling when neofetch isn't installed.


166-181: LGTM - PATH handling is correct.

The directory existence checks before PATH modification are appropriate, and prepending user directories ensures user-local binaries take precedence over system-wide ones.

iso/live-build/config/includes.chroot/etc/pam.d/sshd (1)

1-15: LGTM!

The PAM configuration follows standard Debian patterns for sshd. The dual pam_motd.so approach correctly displays both dynamic (/run/motd.dynamic) and static MOTD content. The module_unknown=ignore handling for pam_selinux.so ensures graceful fallback on systems without SELinux.

iso/live-build/config/hooks/live/50-cortex-branding.hook.chroot (2)

1-11: Overall script structure is well-organized.

The branding hook follows good practices: set -e for early failure detection, clear section comments, and graceful fallbacks for missing commands. The modular approach with verification warnings for missing assets aids debugging during ISO builds.

Also applies to: 366-374


159-162: This code is intentional branding behavior; no action needed.

The MOTD script disabling is documented through the section header, file purpose, and immediate replacement with a custom Cortex MOTD. Disabling default MOTD scripts does not break system behavior—MOTD only displays informational messages at login. Being selective would defeat the purpose of a branding hook that replaces default Debian messaging with Cortex identity.

Likely an incorrect or invalid review comment.

Comment on lines +98 to +124
[org/gnome/terminal/legacy/profiles:/:cortex]
audible-bell=false
background-color='#0D0D0D'
bold-color='#06B6D4'
bold-color-same-as-fg=false
bold-is-bright=true
cursor-background-color='#06B6D4'
cursor-colors-set=true
cursor-foreground-color='#0D0D0D'
cursor-shape='ibeam'
default-size-columns=120
default-size-rows=32
font='JetBrains Mono 11'
foreground-color='#E5E5E5'
highlight-background-color='#6B21A8'
highlight-colors-set=true
highlight-foreground-color='#FFFFFF'
palette=['#0D0D0D', '#EF4444', '#22C55E', '#EAB308', '#3B82F6', '#A855F7', '#06B6D4', '#A3A3A3', '#525252', '#F87171', '#4ADE80', '#FACC15', '#60A5FA', '#C084FC', '#22D3EE', '#F5F5F5']
scroll-on-output=false
scrollback-lines=10000
use-system-font=false
use-theme-colors=false
visible-name='Cortex'
[org/gnome/terminal/legacy/profiles:]
default='cortex'
list=['cortex']
Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Potential issue | 🟡 Minor

🧩 Analysis chain

🏁 Script executed:

# First, check the file and its full context
head -n 150 iso/live-build/config/hooks/live/99-configure-gnome.hook.chroot | tail -n 60

Repository: cortexlinux/cortex-distro

Length of output: 1907


🏁 Script executed:

# Search for how GNOME Terminal profiles are configured in the repository
rg -n "profiles" iso/live-build/config/hooks/live/99-configure-gnome.hook.chroot -A 2 -B 2

Repository: cortexlinux/cortex-distro

Length of output: 422


🌐 Web query:

GNOME Terminal dconf profile format UUID vs arbitrary names

💡 Result:

Short answer

  • GNOME Terminal stores each profile under a path that uses a UUID (GUID): /org/gnome/terminal/legacy/profiles:/:/ — the profile list key (/org/gnome/terminal/legacy/profiles:/ list) contains those UUID strings. The human-visible name is stored separately as the visible-name key inside the profile. You cannot use arbitrary plain names as the profile path; use a UUID (you can generate one with uuidgen). [1][2][3]

Sources

  • GNOME Terminal FAQ (profile ID & gsettings examples). [1]
  • Explanation of relocatable schema and profile storage (org.gnome.Terminal.Legacy.Profile at /.../profiles:/:/). [2]
  • Examples showing gsettings/dconf paths and profile UUID list. [3]

🌐 Web query:

org/gnome/terminal/legacy/profiles dconf configuration requirements

💡 Result:

Summary — what dconf expects for /org/gnome/terminal/legacy/profiles:

  • Top-level path: /org/gnome/terminal/legacy/profiles:/ contains at least:
    • default — string UUID of the default profile. [1]
    • list — array of profile UUID strings (order not important). [1][3]
  • Each profile is stored under the path template:
    /org/gnome/terminal/legacy/profiles:/:/
    and must be a keyfile section (i.e., keys/values under that path). Typical keys come from the org.gnome.Terminal.Legacy.Profile schema (visible-name, font, use-system-font, palette, background-color, foreground-color, use-theme-colors, default-size-columns/rows, etc.). Types follow the schema (strings, booleans, integers, arrays). [2][3]
  • dconf/gsettings syntax notes:
    • dconf paths include the colon after profiles (profiles:/). Use the full path when writing with dconf. Example: dconf write /org/gnome/terminal/legacy/profiles:/:/font "'Monospace 12'". Note the nested quoting for string values. [2][3]
    • Equivalent gsettings form uses the schema prefix: gsettings set org.gnome.Terminal.Legacy.Profile:/org/gnome/terminal/legacy/profiles:/:/ . [2]
  • Backup/restore and reset:
    • Use dconf dump /org/gnome/terminal/ to export, dconf load /org/gnome/terminal/ to import, and dconf reset -f /org/gnome/terminal/ to wipe. [3]
  • dconf profile/database behavior:
    • System/user databases and dconf profiles determine precedence (user-db then system-db lines in /etc/dconf/profile/*). Changes require re-login to apply profile changes. [1]

If you want, I can show exact dconf write/gsettings commands for a specific key or produce a sample dconf dump for one profile.

Sources: [1] GNOME dconf profiles docs; [2] gsettings/dconf notes/example usage; [3] example dconf dump and keys.


Terminal profile must use a valid UUID, not arbitrary names.

GNOME Terminal identifies profiles by UUID in dconf, not arbitrary names. The path [org/gnome/terminal/legacy/profiles:/:cortex] and default='cortex' will be silently ignored. The profile path must follow the format [org/gnome/terminal/legacy/profiles:/:UUID] where UUID is a valid UUID string (e.g., generated with uuidgen), and both default and list values must reference this UUID.

Proposed fix (example with generated UUID)
-[org/gnome/terminal/legacy/profiles:/:cortex]
+[org/gnome/terminal/legacy/profiles:/:b1dcc9dd-5262-4d8d-a863-c897e6d979b9]
 audible-bell=false
 ...
 visible-name='Cortex'

 [org/gnome/terminal/legacy/profiles:]
-default='cortex'
-list=['cortex']
+default='b1dcc9dd-5262-4d8d-a863-c897e6d979b9'
+list=['b1dcc9dd-5262-4d8d-a863-c897e6d979b9']
🤖 Prompt for AI Agents
In @iso/live-build/config/hooks/live/99-configure-gnome.hook.chroot around lines
98 - 124, The GNOME Terminal profile block uses an invalid literal name
"cortex"; replace the profile path [org/gnome/terminal/legacy/profiles:/:cortex]
with a valid UUID-based path (e.g.,
[org/gnome/terminal/legacy/profiles:/:<UUID>]) and update default='cortex' and
list=['cortex'] to reference that same UUID string; keep visible-name='Cortex'
unchanged and generate the UUID with uuidgen (or similar) so all three places
(profile section name, default, and list) use the identical UUID.

session optional pam_motd.so motd=/run/motd.dynamic
session optional pam_motd.so noupdate
session optional pam_mail.so standard
session required pam_env.so user_readenv=1 envfile=/etc/default/locale
Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Potential issue | 🟠 Major

🧩 Analysis chain

🌐 Web query:

pam_env user_readenv deprecated security vulnerability

💡 Result:

Short answer: Yes — pam_env's user_readenv is deprecated because it’s insecure, and its use has been linked to real vulnerabilities (e.g. a DoS in cockpit and local privilege-escalation issues). Mitigation: remove/disable user_readenv (set user_readenv=0 or remove the option) or upgrade to a Linux-PAM release where it’s disabled by default (>=1.4.0) — the option was marked deprecated in 1.5.0 and planned for removal. Sources and details below.

Key references

  • CVE-2024-6126 (cockpit): authenticated users can kill processes when pam_env user_readenv is enabled — disable user_readenv to mitigate. [1]
  • CVE-2025-6018 / advisories: LPE/vector where pam_env reading user ~/.pam_environment (user_readenv) enabled by default on some distros; vendors recommend setting user_readenv=0 and note it is deprecated and removed in newer PAM. [2][3]
  • pam_env manpage / upstream: documents user_readenv, notes default-off in many builds and that the functionality is deprecated since PAM 1.5.0. [4]

Recommended actions (pick all that apply for your environment)

  • Check PAM config files in /etc/pam.d/ (and any packaged pambase/pambase-like configs) for pam_env.so user_readenv=1 and remove that option or set user_readenv=0. [4]
  • Upgrade Linux-PAM to a version where user_readenv is disabled by default (>=1.4.0) or to a release that removed the feature. [3][4]
  • Apply vendor/security advisories and patches for affected packages (e.g., cockpit, distro packages) and follow distro-specific guidance. [1][2]
  • If you must allow user environment reading for specific services, evaluate the PAM stack ordering (avoid calling pam_env before modules like pam_systemd/polkit) and restrict which services invoke pam_env. [2]

If you want, tell me your distribution and I’ll list exactly which PAM files to check and give the precise edits/commands.

Sources:
[1] NVD / CVE-2024-6126 (cockpit DoS).
[2] Wiz / CVE-2025-6018 advisory (pam_env user_readenv LPE analysis).
[3] Snyk advisory and recommendations (workaround: set user_readenv=0; deprecated in 1.5.0).
[4] pam_env manpage (notes user_readenv deprecated since 1.5.0).


Remove user_readenv=1 due to deprecated security vulnerability.

The user_readenv=1 option has been deprecated since PAM 1.5.0 and is linked to multiple security vulnerabilities:

  • CVE-2024-6126: Authenticated users can kill arbitrary processes when user_readenv is enabled
  • CVE-2025-6018: Local privilege escalation vector through user-controlled ~/.pam_environment file manipulation

Security advisories from Wiz, Snyk, and Linux-PAM upstream recommend removing this option (or setting user_readenv=0). Modern PAM versions (>=1.4.0) disable it by default.

Proposed fix
-session    required   pam_env.so user_readenv=1 envfile=/etc/default/locale
+session    required   pam_env.so envfile=/etc/default/locale
📝 Committable suggestion

‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.

Suggested change
session required pam_env.so user_readenv=1 envfile=/etc/default/locale
session required pam_env.so envfile=/etc/default/locale
🤖 Prompt for AI Agents
In @iso/live-build/config/includes.chroot/etc/pam.d/login at line 16, Remove the
deprecated and insecure user_readenv=1 option from the pam_env.so session line:
locate the line that invokes pam_env.so (currently "session required pam_env.so
user_readenv=1 envfile=/etc/default/locale") and either drop the user_readenv=1
entirely or change it to user_readenv=0 so the entry reads "session required
pam_env.so envfile=/etc/default/locale" (or with user_readenv=0) to mitigate the
CVE issues.

Copy link

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 3

🤖 Fix all issues with AI agents
In `@iso/live-build/config/hooks/live/50-cortex-branding.hook.chroot`:
- Around line 92-97: The current post-check using "$?" after running
plymouth-set-default-theme is ineffective under set -e because the script will
exit on failure; replace the two-step run+exit-code-check with a non-fatal
failure idiom so failures emit a warning but do not abort: run
plymouth-set-default-theme cortex with a failure-safe construct (e.g., a logical
OR fallback or an if ! ...; then ...; fi) so that plymouth-set-default-theme can
fail without causing immediate exit and the warning is printed (refer to the
plymouth-set-default-theme invocation and the surrounding if command -v ...
block).
- Around line 399-411: Replace the current "run command then check $? later"
pattern for update-initramfs: either run the command inside a conditional (if
update-initramfs -u -k all; then ... else echo "ERROR: Failed to update
initramfs" ; exit 1; fi) or immediately capture its exit code (rc=$? right after
update-initramfs) before any potential early-exit behavior; update the echo/exit
logic around the update-initramfs invocation and remove the delayed if [ $? -eq
0 ] check so the failure message and exit happen reliably.

In `@scripts/build.sh`:
- Around line 742-748: The unquoted glob in the chmod call can break on
filenames with spaces or if the glob expands to nothing; update the post-copy
permission step (the block that calls copy_glob_if_exists and then chmod on
"${pkg_dir}/etc/update-motd.d/"*) to iterate safely over matched files or use a
safe find/exec pattern: ensure you check for existence before chmod and quote
each filename when changing permissions (refer to the copy_glob_if_exists
invocation and the chmod invocation) so filenames with spaces are handled and
the case of no matches does not cause an unintended chmod argument.
♻️ Duplicate comments (9)
iso/live-build/config/hooks/live/50-cortex-branding.hook.chroot (3)

9-9: Consider using stricter bash settings.

Using set -euo pipefail instead of just set -e would catch unset variable references and pipeline failures, making the script more robust.

-set -e
+set -euo pipefail

27-30: Domain inconsistency across branding files persists.

These URLs use cortex.io, but line 334 (MOTD footer) references cortexlinux.com and docs.cortexlinux.com. Standardize on one canonical domain for consistency.


127-130: Bug: GRUB_DISTRIBUTOR will not be appended if missing.

The sed command returns exit code 0 even when the pattern isn't matched (no substitutions made). The || echo fallback will never execute. This issue was flagged in previous reviews.

Apply the same fix pattern used for GRUB_THEME on lines 121-125:

     # Also set timeout and other branding options
-    sed -i 's|^GRUB_DISTRIBUTOR=.*|GRUB_DISTRIBUTOR="Cortex Linux"|' /etc/default/grub || \
-        echo 'GRUB_DISTRIBUTOR="Cortex Linux"' >> /etc/default/grub
+    if grep -q "^GRUB_DISTRIBUTOR=" /etc/default/grub; then
+        sed -i 's|^GRUB_DISTRIBUTOR=.*|GRUB_DISTRIBUTOR="Cortex Linux"|' /etc/default/grub
+    else
+        echo 'GRUB_DISTRIBUTOR="Cortex Linux"' >> /etc/default/grub
+    fi
scripts/build.sh (6)

63-72: Unquoted glob and missing -r flag remain unaddressed.

This was flagged in a prior review. The unquoted glob can break on filenames with spaces, and cp without -r will fail if directories are matched.


136-140: dpkg-deb still treated as optional despite being required for branding-package.

This was flagged in a prior review. cmd_branding_package at line 759 calls dpkg-deb --build which will fail if the tool is missing.


287-304: Arithmetic expansion of $? after || still captures wrong value.

This was flagged in a prior review. In func || errors=$((errors + $?)), the $? captures the exit status of the || short-circuit (always 0 when the assignment runs), not the function's return value.


549-558: Unconditional sudo lb build still present.

This was flagged in a prior review. Running sudo when already root fails in many CI environments. Use conditional sudo based on $EUID.


615-625: Unconditional sudo lb clean has the same issue.

This was flagged in a prior review alongside sudo lb build. Both need conditional sudo based on $EUID.


695-704: Unconditional chmod on postinst and prerm still present.

This was flagged in a prior review. If either file is missing from the source DEBIAN/ directory, the script will fail.

🧹 Nitpick comments (1)
iso/live-build/config/hooks/live/50-cortex-branding.hook.chroot (1)

263-264: Consider PCRE availability.

grep -oP (Perl-Compatible Regular Expressions) may not be available on all systems. If busybox grep or older GNU grep is present without PCRE support, these lines will fail silently (stderr redirected).

Consider using extended regex (grep -oE) or basic pattern matching as a fallback:

-        UPDATES=$(grep -oP '\d+ updates' "$UPDATES_FILE" 2>/dev/null | grep -oP '\d+' || echo "0")
-        SECURITY=$(grep -oP '\d+ security' "$UPDATES_FILE" 2>/dev/null | grep -oP '\d+' || echo "0")
+        UPDATES=$(grep -oE '[0-9]+ updates' "$UPDATES_FILE" 2>/dev/null | grep -oE '[0-9]+' || echo "0")
+        SECURITY=$(grep -oE '[0-9]+ security' "$UPDATES_FILE" 2>/dev/null | grep -oE '[0-9]+' || echo "0")
📜 Review details

Configuration used: defaults

Review profile: CHILL

Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between 6564a1a and 79c2cda.

📒 Files selected for processing (2)
  • iso/live-build/config/hooks/live/50-cortex-branding.hook.chroot
  • scripts/build.sh
🧰 Additional context used
🧠 Learnings (1)
📚 Learning: 2026-01-12T20:51:13.828Z
Learnt from: CR
Repo: cortexlinux/cortex PR: 0
File: AGENTS.md:0-0
Timestamp: 2026-01-12T20:51:13.828Z
Learning: Applies to **/*.py : Do not use silent sudo - require explicit user confirmation for privilege escalation

Applied to files:

  • scripts/build.sh
🔇 Additional comments (9)
iso/live-build/config/hooks/live/50-cortex-branding.hook.chroot (4)

49-61: LGTM!

The APT repository configuration is appropriately commented out with a clear TODO, avoiding the previously flagged duplicate repository issue.


137-149: LGTM!

Wallpaper verification with appropriate warnings for missing assets.


151-177: LGTM!

GDM branding configuration is well-guarded with directory and command existence checks.


343-392: LGTM!

Neofetch configuration with custom ASCII art is well-structured.

scripts/build.sh (5)

7-47: LGTM: Clean configuration and logging setup.

The use of set -euo pipefail for strict error handling and the modular logging functions are well-structured.


499-526: LGTM: Configurable compression and well-structured live-build options.

The SQUASHFS_COMP variable for fast CI builds (lz4) vs smaller release builds (xz) is a good pattern. The boot parameters and live-build configuration are comprehensive.


320-420: LGTM: Comprehensive test suite with clear pass/fail reporting.

The test function covers key build artifacts (preseed, provisioning, branding, hooks, package controls) with proper syntax validation. The [ -f "$hook" ] check handles the glob edge case when no files match.


816-857: LGTM: Clean command dispatch pattern.

The entry point is well-structured with a clear case-based dispatch, proper help aliases, and argument passthrough to subcommands.


470-497: LGTM: Good validation for required GRUB theme file.

The explicit check for theme.txt with a clear error message (lines 483-489) addresses the branding validation concern from prior reviews.

✏️ Tip: You can disable this entire section by setting review_details to false in your review settings.

Comment on lines +92 to +97
if command -v plymouth-set-default-theme &>/dev/null; then
plymouth-set-default-theme cortex
if [ $? -ne 0 ]; then
echo " WARNING: plymouth-set-default-theme failed"
fi
fi
Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Potential issue | 🟡 Minor

Ineffective exit code check due to set -e.

With set -e active, if plymouth-set-default-theme cortex fails on line 93, the script exits immediately before line 94 executes. The if [ $? -ne 0 ] check is dead code.

If you want to warn and continue on failure:

 if command -v plymouth-set-default-theme &>/dev/null; then
-    plymouth-set-default-theme cortex
-    if [ $? -ne 0 ]; then
-        echo "    WARNING: plymouth-set-default-theme failed"
-    fi
+    if ! plymouth-set-default-theme cortex; then
+        echo "    WARNING: plymouth-set-default-theme failed"
+    fi
 fi
🤖 Prompt for AI Agents
In `@iso/live-build/config/hooks/live/50-cortex-branding.hook.chroot` around lines
92 - 97, The current post-check using "$?" after running
plymouth-set-default-theme is ineffective under set -e because the script will
exit on failure; replace the two-step run+exit-code-check with a non-fatal
failure idiom so failures emit a warning but do not abort: run
plymouth-set-default-theme cortex with a failure-safe construct (e.g., a logical
OR fallback or an if ! ...; then ...; fi) so that plymouth-set-default-theme can
fail without causing immediate exit and the warning is printed (refer to the
plymouth-set-default-theme invocation and the surrounding if command -v ...
block).

Comment on lines +399 to +411
if command -v update-initramfs &>/dev/null; then
# Update ALL kernels
update-initramfs -u -k all

if [ $? -eq 0 ]; then
echo " Initramfs updated successfully"
else
echo " ERROR: Failed to update initramfs"
exit 1
fi
else
echo " WARNING: update-initramfs command not found"
fi
Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Potential issue | 🟡 Minor

Ineffective exit code check pattern.

Same issue as the Plymouth section: with set -e, if update-initramfs -u -k all fails, the script exits before the if [ $? -eq 0 ] check. The error message on line 406-407 will never be displayed.

If you want custom error handling:

 if command -v update-initramfs &>/dev/null; then
     # Update ALL kernels
-    update-initramfs -u -k all
-
-    if [ $? -eq 0 ]; then
+    if update-initramfs -u -k all; then
         echo "    Initramfs updated successfully"
     else
         echo "    ERROR: Failed to update initramfs"
         exit 1
     fi
 else
     echo "    WARNING: update-initramfs command not found"
 fi
🤖 Prompt for AI Agents
In `@iso/live-build/config/hooks/live/50-cortex-branding.hook.chroot` around lines
399 - 411, Replace the current "run command then check $? later" pattern for
update-initramfs: either run the command inside a conditional (if
update-initramfs -u -k all; then ... else echo "ERROR: Failed to update
initramfs" ; exit 1; fi) or immediately capture its exit code (rc=$? right after
update-initramfs) before any potential early-exit behavior; update the echo/exit
logic around the update-initramfs invocation and remove the delayed if [ $? -eq
0 ] check so the failure message and exit happen reliably.

Comment on lines +742 to +748
# Copy MOTD scripts
log "Copying MOTD scripts..."
if [ -d "${BRANDING_DIR}/motd" ]; then
if copy_glob_if_exists "${BRANDING_DIR}/motd/*" "${pkg_dir}/etc/update-motd.d/"; then
chmod 755 "${pkg_dir}/etc/update-motd.d/"*
fi
fi
Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Potential issue | 🟡 Minor

Unquoted glob in chmod can break on filenames with spaces or empty matches.

Line 746 uses an unquoted glob "${pkg_dir}/etc/update-motd.d/"* which will fail if filenames contain spaces or if no files were copied.

🔧 Proposed fix using a loop or find
     # Copy MOTD scripts
     log "Copying MOTD scripts..."
     if [ -d "${BRANDING_DIR}/motd" ]; then
         if copy_glob_if_exists "${BRANDING_DIR}/motd/*" "${pkg_dir}/etc/update-motd.d/"; then
-            chmod 755 "${pkg_dir}/etc/update-motd.d/"*
+            find "${pkg_dir}/etc/update-motd.d/" -type f -exec chmod 755 {} +
         fi
     fi
🤖 Prompt for AI Agents
In `@scripts/build.sh` around lines 742 - 748, The unquoted glob in the chmod call
can break on filenames with spaces or if the glob expands to nothing; update the
post-copy permission step (the block that calls copy_glob_if_exists and then
chmod on "${pkg_dir}/etc/update-motd.d/"*) to iterate safely over matched files
or use a safe find/exec pattern: ensure you check for existence before chmod and
quote each filename when changing permissions (refer to the copy_glob_if_exists
invocation and the chmod invocation) so filenames with spaces are handled and
the case of no matches does not cause an unintended chmod argument.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

3 participants