Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -16,6 +16,7 @@ podman_scp.*
# VM testing
_vm_build/
.vm/
.cache

# ISO files
*.iso
Expand Down
99 changes: 99 additions & 0 deletions AGENTS.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,99 @@
# AGENTS.md

This file provides guidance to coding agents when working with code in this repository.

## Project Overview

Hypercube is a container-based Linux distribution built on Fedora Atomic (via Universal Blue's `base-main:43`). It's a keyboard-first Hyprland desktop with vim keybindings, Tokyo Night theming, and developer tooling. The image is built as an OCI container and deployed via bootc.

## Build Commands

```bash
just build # Build container image locally (rootless Podman)
just build-force # Build without cache
just build-ghcr # Build for GHCR push (rootful)
just run # Run container interactively for testing
just lint # Shellcheck all .sh files
just format # Format all .sh files with shfmt
just check # Validate Justfile syntax
just fix # Fix Justfile formatting
```

### VM/ISO Testing

```bash
just build-qcow2-fast # Create disk image via bootc install
just run-qcow2 # Test qcow2 in VM with virt-manager
just build-iso-local # Build ISO from local image
just run-iso <file> # Test ISO in QEMU VM
```

### Package Version Scripts

```bash
./scripts/packages/check-upstream-versions.sh # Detect upstream updates
./scripts/packages/check-copr-versions.sh # Compare spec vs COPR builds
./scripts/packages/test-all.sh # Run package script tests
```

## Architecture

### Build Pipeline

The `Containerfile` defines a multi-stage build:

1. **Stage `ctx`**: Aggregates `system_files/` and `build_files/` into `/ctx`
2. **Main stage**: Builds from `ghcr.io/ublue-os/base-main:43`, mounts `dot_files/` at `/dot_files`, runs `build.sh`

`build_files/shared/build.sh` orchestrates the build:

- Rsyncs `system_files/shared/` to the root filesystem
- Executes numbered scripts (`00-*.sh` through `99-*.sh`) sequentially from each phase directory

### Build Phases (in order)

| Directory | Purpose |
| ------------------------ | ----------------------------------------------------- |
| `build_files/base/` | Kernel, greetd, audio, networking, portals |
| `build_files/hyprland/` | Compositor, shell, terminal, editor, CLI tools |
| `build_files/dx/` | Language servers, containers (Distrobox/Podman) |
| `build_files/apps/` | Applications (Steam, etc.) |
| `build_files/hypercube/` | Branding, packages, theming, config deployment, tests |

### Configuration System

Configs follow XDG Base Directory specification:

- `dot_files/` → deployed to `/usr/share/hypercube/config/` (system defaults)
- `/usr/lib/environment.d/60-hypercube-xdg.conf` adds this path to `XDG_CONFIG_DIRS`
- Users override via `~/.config/`
- Fish shell configs go to `/etc/fish/` (Fish doesn't use XDG_CONFIG_DIRS)
- System-level files in `system_files/shared/` mirror the target filesystem path (e.g., `system_files/shared/etc/greetd/config.toml` → `/etc/greetd/config.toml`)

### Build Validation

`build_files/hypercube/99-tests.sh` validates the build by checking:

- Required packages are installed (greetd, hyprland, ghostty, neovim, etc.)
- Required files exist (branding, configs, themes, plymouth)
- os-release contains `ID=hypercube`
- Required services are enabled (greetd, NetworkManager)

Failures exit with code 1, preventing the image from being published.

### COPR Packages

26 custom packages are maintained in `packages/` with RPM spec files. Package metadata, dependencies, and build ordering are defined in `scripts/packages/config.sh`. Packages are built in 5 dependency-ordered batches.

## Shell Script Conventions

- All build scripts use `set -ouex pipefail`
- Scripts are named with numeric prefixes for execution ordering
- Package installation uses `dnf5 -y install`
- COPR repos are enabled with `dnf5 -y copr enable owner/repo`

## Key Integration Points

- **Titanoboa** (`_titanoboa/`): External ISO builder, cloned on demand by `just build-iso-*`
- **Cosign** (`cosign.pub`): Image signature verification
- **GitHub Actions** (`.github/workflows/build.yml`): Builds on PR and daily, pushes to GHCR on merge to main
3 changes: 1 addition & 2 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -49,7 +49,6 @@
| Package | Status |
|---------|--------|
| quickshell | [![quickshell](https://copr.fedorainfracloud.org/coprs/binarypie/hypercube/package/quickshell/status_image/last_build.png)](https://copr.fedorainfracloud.org/coprs/binarypie/hypercube/package/quickshell/) |
| regreet | [![regreet](https://copr.fedorainfracloud.org/coprs/binarypie/hypercube/package/regreet/status_image/last_build.png)](https://copr.fedorainfracloud.org/coprs/binarypie/hypercube/package/regreet/) |
| livesys-scripts | [![livesys-scripts](https://copr.fedorainfracloud.org/coprs/binarypie/hypercube/package/livesys-scripts/status_image/last_build.png)](https://copr.fedorainfracloud.org/coprs/binarypie/hypercube/package/livesys-scripts/) |

</details>
Expand Down Expand Up @@ -90,7 +89,7 @@ Tokyo Night color scheme everywhere:
- Terminal emulators
- Neovim and all CLI tools
- Plymouth boot animation
- ReGreet login screen
- Hypercube greeter
- System-wide dark mode enforced

## Screenshots
Expand Down
14 changes: 14 additions & 0 deletions build_files/apps/01-apps.sh
Original file line number Diff line number Diff line change
Expand Up @@ -9,4 +9,18 @@ echo "Installing applications..."
### Web Browser
dnf5 -y install firefox

### Flatpak Applications (system-wide, baked into image)
flatpak remote-add --if-not-exists --system flathub https://flathub.org/repo/flathub.flatpakrepo
flatpak install --system --noninteractive flathub \
io.github.kolunmi.Bazaar \
com.usebottles.bottles \
io.github.dvlv.boxbuddyrs \
org.gnome.Boxes \
com.github.tchx84.Flatseal \
be.alexandervanhee.gradia \
io.github.seadve.Kooha \
io.podman_desktop.PodmanDesktop \
org.mozilla.Thunderbird \
io.github.flattool.Warehouse

echo "Applications installed successfully"
12 changes: 5 additions & 7 deletions build_files/base/01-base-system.sh
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
#!/bin/bash
# Hypercube Base System
# Installs core system components, display manager (greetd + regreet), and hardware support
# Installs core system components, display manager (greetd + hypercube-utils), and hardware support

set -ouex pipefail

Expand All @@ -12,14 +12,12 @@ dnf5 -y clean all
### Enable Hypercube COPR for custom packages
dnf5 -y copr enable binarypie/hypercube

### Display Manager: greetd + regreet
# regreet from binarypie/hypercube COPR
# cage is a minimal Wayland compositor to host regreet and the first-boot wizard
### Display Manager: greetd + hypercube-utils
# hypercube-utils provides hypercube-greeter and hypercube-onboard (run directly on TTY)
dnf5 -y install \
greetd \
greetd-selinux \
cage \
regreet
hypercube-utils

### Desktop Portals & Integration
dnf5 -y install \
Expand Down Expand Up @@ -84,7 +82,7 @@ dnf5 -y install \
gvfs-gphoto2 \
gvfs-smb

### Flatpak (ensure latest version for preinstall.d support)
### Flatpak
dnf5 -y install flatpak

### Homebrew integration (from ublue COPR)
Expand Down
4 changes: 0 additions & 4 deletions build_files/hypercube/03-hypercube-configs.sh
Original file line number Diff line number Diff line change
Expand Up @@ -78,10 +78,6 @@ install -Dm644 "${CONFIG_DIR}/gtk-4.0/settings.ini" /etc/xdg/gtk-4.0/settings.in
install -Dm644 "${CONFIG_DIR}/qt6ct/qt6ct.conf" /etc/xdg/qt6ct/qt6ct.conf
install -Dm644 "${CONFIG_DIR}/qt6ct/colors/TokyoNight.conf" /usr/share/qt6ct/colors/TokyoNight.conf

### ReGreet login greeter configuration (Tokyo Night themed)
install -Dm644 "${CONFIG_DIR}/regreet/regreet.toml" /etc/greetd/regreet.toml
install -Dm644 "${CONFIG_DIR}/regreet/regreet.css" /etc/greetd/regreet.css

### Enable xdg-desktop-portal-gtk for dark mode detection (Firefox, etc.)
# This portal provides the org.freedesktop.appearance.color-scheme setting
systemctl --global enable xdg-desktop-portal-gtk.service
Expand Down
3 changes: 1 addition & 2 deletions build_files/hypercube/99-tests.sh
Original file line number Diff line number Diff line change
Expand Up @@ -10,8 +10,7 @@ echo "Running Hypercube validation tests..."
REQUIRED_PACKAGES=(
# Display manager
"greetd"
"regreet"
"cage"
"hypercube-utils"
# Hyprland stack
"hyprland"
"hyprlock"
Expand Down
29 changes: 29 additions & 0 deletions dot_files/hypercube-onboard/onboard.toml
Original file line number Diff line number Diff line change
@@ -0,0 +1,29 @@
# Hypercube Onboarding Configuration
# Used by hypercube-onboard during first boot (greetd initial_session)

[user]
# User creation settings
groups = ["wheel"]
shell = "/usr/bin/fish"
min_password_length = 8

[locale]
# Default locale settings
language = "en_US.UTF-8"
keyboard_layout = "us"

[timezone]
# Timezone preferences
use_ntp = true

[completion]
# What to do when onboarding finishes
action = "reboot"
remove_initial_session = true

[updates]
# System updates to run during onboarding

[[updates.commands]]
description = "System update"
command = "ujust update"
107 changes: 107 additions & 0 deletions dot_files/hypr/hyprland-kiosk.conf
Original file line number Diff line number Diff line change
@@ -0,0 +1,107 @@
# Hyprland Kiosk Mode Configuration
# Used for desktop kiosk mode applications
# - No keybindings (locked down)
# - Single fullscreen application
# - Exits when the application closes
#
# Usage: Set KIOSK_CMD environment variable before launching
# KIOSK_CMD="your-app" Hyprland -c /path/to/hyprland-kiosk.conf

################
### MONITORS ###
################

# Enable all monitors - kiosk app shows on primary, others show wallpaper
monitor=,preferred,auto,auto

#################
### AUTOSTART ###
#################

# Wallpaper on all monitors
exec-once = hyprpaper -c /usr/share/hypercube/config/hypr/hyprpaper.conf

# Launch the kiosk application from environment variable
exec-once = $KIOSK_CMD

# Exit Hyprland when the last window closes
exec-once = handle=$(hyprctl -j clients | jq -r '.[0].address // empty'); while [ -n "$handle" ]; do sleep 0.5; handle=$(hyprctl -j clients | jq -r '.[0].address // empty'); done; hyprctl dispatch exit

#############################
### ENVIRONMENT VARIABLES ###
#############################

env = XCURSOR_SIZE,24
env = HYPRCURSOR_SIZE,24

#####################
### LOOK AND FEEL ###
#####################

general {
gaps_in = 0
gaps_out = 0
border_size = 0
col.active_border = rgba(00000000)
col.inactive_border = rgba(00000000)
resize_on_border = false
allow_tearing = false
layout = dwindle
}

decoration {
rounding = 0
active_opacity = 1.0
inactive_opacity = 1.0
shadow {
enabled = false
}
blur {
enabled = false
}
}

animations {
enabled = false
}

dwindle {
pseudotile = false
preserve_split = true
}

misc {
force_default_wallpaper = 0
disable_hyprland_logo = true
}

#############
### INPUT ###
#############

input {
kb_layout = us
kb_variant =
kb_model =
kb_options =
kb_rules =
follow_mouse = 1
sensitivity = 0
touchpad {
natural_scroll = false
}
}

###################
### KEYBINDINGS ###
###################

# No keybindings - kiosk mode is locked down

##############################
### WINDOWS AND WORKSPACES ###
##############################

# Force all windows to fullscreen
windowrule = fullscreen, match:class .*
windowrule = nofocus, match:class ^$, match:title ^$
3 changes: 2 additions & 1 deletion dot_files/nvim/Containerfile
Original file line number Diff line number Diff line change
Expand Up @@ -92,7 +92,8 @@ RUN brew install \
jq \
yq \
bat \
eza
eza \
just

# =============================================================================
# LAYER 4: Languages via Homebrew
Expand Down
19 changes: 4 additions & 15 deletions dot_files/quickshell/modules/bar/StatusBar.qml
Original file line number Diff line number Diff line change
Expand Up @@ -152,8 +152,7 @@ PanelWindow {
cursorShape: Qt.PointingHandCursor
onClicked: Root.GlobalStates.toggleSidebarLeft(root.targetScreen, "updates")

property bool isRunning: Services.Updates.preinstallRunning
property bool needsAttention: Services.Updates.needsAttention
property bool needsAttention: Services.Updates.updateCount > 0

Rectangle {
anchors.fill: parent
Expand All @@ -169,23 +168,13 @@ PanelWindow {

Common.Icon {
anchors.centerIn: parent
name: updatesButton.isRunning
? Common.Icons.icons.refresh
: (updatesButton.needsAttention
? Common.Icons.icons.download
: Common.Icons.icons.checkCircle)
name: updatesButton.needsAttention
? Common.Icons.icons.update
: Common.Icons.icons.checkCircle
size: Common.Appearance.sizes.iconMedium
color: updatesButton.needsAttention
? Common.Appearance.m3colors.primary
: Common.Appearance.m3colors.onSurfaceVariant

RotationAnimation on rotation {
running: updatesButton.isRunning
from: 0
to: 360
duration: 1000
loops: Animation.Infinite
}
}
}

Expand Down
Loading