Compare commits

..

1 Commits

Author SHA1 Message Date
b9f56ff57d feat(home-manager): Add platform compatibility guards to cross-platform roles
Add lib.optionals pkgs.stdenv.isLinux guards to roles that contain
Linux-only packages or services to prevent build failures on Darwin:

- communication: Guard Electron apps (element-desktop, fluffychat,
  nextcloud-talk-desktop) that don't build on Darwin due to electron
  build-from-source limitations
- kdeconnect: Guard entire config block since services.kdeconnect
  requires D-Bus and systemd (Linux-only)
- sync: Guard syncthingtray package (requires Linux system tray)
- email: Guard systemd.user.services/timers (Darwin uses launchd)
- desktop: Guard Linux-only packages, services, and KDE-specific
  configurations including gnome-keyring, systemd services, and
  XDG mime associations

Implements bead: nixos-configs-tcu
2026-01-10 13:09:47 -08:00
6 changed files with 127 additions and 236 deletions

View File

@@ -1,130 +0,0 @@
---
description: Import open Gitea issues as beads, skipping already-imported ones
---
# Import Gitea Issues as Beads
This skill imports open Gitea issues as beads, checking for duplicates to avoid re-importing already tracked issues.
## Prerequisites
- `tea` CLI must be installed and configured for the repository
- `bd` (beads) CLI must be installed
- Must be in a git repository with a Gitea/Forgejo remote
## Workflow
### Step 1: Get open Gitea issues
List all open issues using `tea`:
```bash
tea issues
```
This returns a table with columns: INDEX, TITLE, LABELS, MILESTONE
### Step 2: Get existing beads
List all current beads to check what's already imported:
```bash
bd list
```
Also check bead notes for issue URLs to identify imports:
```bash
bd list --json | jq -r '.[] | select(.notes != null) | .notes' | grep -oP 'issues/\K\d+'
```
### Step 3: Check for already-linked PRs
Check if any open PRs reference beads (skip these issues as they're being worked on):
```bash
tea pr list
```
Look for PRs with:
- Bead ID in title: `[nixos-configs-xxx]`
- Bead reference in body: `Implements bead:` or `Bead ID:`
### Step 4: For each untracked issue, create a bead
For each issue not already tracked:
1. **Get full issue details**:
```bash
tea issue [ISSUE_NUMBER]
```
2. **Determine bead type** based on issue content:
- "bug" - if issue mentions bug, error, broken, fix, crash
- "feature" - if issue mentions feature, add, new, enhancement
- "task" - default for other issues
3. **Create the bead**:
```bash
bd add "[ISSUE_TITLE]" \
--type=[TYPE] \
--priority=P2 \
--notes="Gitea issue: [ISSUE_URL]
Original issue description:
[ISSUE_BODY]"
```
Note: The `--notes` flag accepts multi-line content.
### Step 5: Report results
Present a summary:
```
## Gitea Issues Import Summary
### Imported as Beads
| Issue | Title | Bead ID | Type |
|-------|-------|---------|------|
| #5 | Add dark mode | nixos-configs-abc | feature |
| #3 | Config broken on reboot | nixos-configs-def | bug |
### Skipped (Already Tracked)
| Issue | Title | Reason |
|-------|-------|--------|
| #4 | Update flake | Existing bead: nixos-configs-xyz |
| #2 | Refactor roles | PR #7 references bead |
### Skipped (Other)
| Issue | Title | Reason |
|-------|-------|--------|
| #1 | Discussion: future plans | No actionable work |
```
## Type Detection Heuristics
Keywords to detect issue type:
**Bug indicators** (case-insensitive):
- bug, error, broken, fix, crash, fail, issue, problem, wrong, not working
**Feature indicators** (case-insensitive):
- feature, add, new, enhancement, implement, support, request, want, would be nice
**Task** (default):
- Anything not matching bug or feature patterns
## Error Handling
- **tea not configured**: Report error and exit
- **bd not available**: Report error and exit
- **Issue already has bead**: Skip and report in summary
- **Issue is a PR**: Skip (tea shows PRs and issues separately)
## Notes
- Default priority is P2; adjust manually after import if needed
- Issue labels from Gitea are not automatically mapped to bead tags
- Run this periodically to catch new issues
- After import, use `bd ready` to see which beads can be worked on

View File

@@ -4,6 +4,7 @@ with lib;
let
cfg = config.home.roles.communication;
isLinux = pkgs.stdenv.isLinux;
in
{
options.home.roles.communication = {
@@ -12,14 +13,14 @@ in
config = mkIf cfg.enable {
home.packages = [
# Communication apps
# For logging back into google chat (cross-platform)
globalInputs.google-cookie-retrieval.packages.${system}.default
] ++ optionals isLinux [
# Linux-only communication apps (Electron apps don't build on Darwin)
pkgs.element-desktop
# Re-enabled in 25.11 after security issues were resolved
pkgs.fluffychat
pkgs.nextcloud-talk-desktop
# For logging back into google chat
globalInputs.google-cookie-retrieval.packages.${system}.default
];
};
}

View File

@@ -4,6 +4,7 @@ with lib;
let
cfg = config.home.roles.desktop;
isLinux = pkgs.stdenv.isLinux;
in
{
options.home.roles.desktop = {
@@ -12,61 +13,63 @@ in
config = mkIf cfg.enable {
home.packages = with pkgs; [
# Desktop applications
# Cross-platform desktop applications
bitwarden-desktop
dunst
keepassxc
xdg-utils # XDG utilities for opening files/URLs with default applications
] ++ optionals isLinux [
# Linux-only desktop applications
dunst
unstable.ghostty
# Desktop utilities
# Linux-only desktop utilities
feh # Image viewer and wallpaper setter for X11
rofi # Application launcher for X11
solaar # Logitech management software
waybar
wofi # Application launcher for Wayland
xdg-utils # XDG utilities for opening files/URLs with default applications
# System utilities with GUI components
# Linux-only system utilities with GUI components
(snapcast.override { pulseaudioSupport = true; })
# KDE tiling window management
# KDE tiling window management (Linux-only)
kdePackages.krohnkite # Dynamic tiling extension for KWin 6
# KDE PIM applications for email, calendar, and contacts
# KDE PIM applications for email, calendar, and contacts (Linux-only)
kdePackages.kmail
kdePackages.kmail-account-wizard
kdePackages.kmailtransport
kdePackages.korganizer
kdePackages.kaddressbook
kdePackages.kontact
# KDE System components needed for proper integration
# KDE System components needed for proper integration (Linux-only)
kdePackages.kded
kdePackages.systemsettings
kdePackages.kmenuedit
# Desktop menu support
# Desktop menu support (Linux-only)
kdePackages.plasma-desktop # Contains applications.menu
# KDE Online Accounts support
# KDE Online Accounts support (Linux-only)
kdePackages.kaccounts-integration
kdePackages.kaccounts-providers
kdePackages.signond
# KDE Mapping
# KDE Mapping (Linux-only)
kdePackages.marble # Virtual globe and world atlas
# KDE Productivity
# KDE Productivity (Linux-only)
kdePackages.kate # Advanced text editor with syntax highlighting
kdePackages.okular # Universal document viewer (PDF, ePub, etc.)
kdePackages.spectacle # Screenshot capture utility
kdePackages.filelight # Visual disk usage analyzer
# KDE Multimedia
# KDE Multimedia (Linux-only)
kdePackages.gwenview # Image viewer and basic editor
kdePackages.elisa # Music player
# KDE System Utilities
# KDE System Utilities (Linux-only)
kdePackages.ark # Archive manager (zip, tar, 7z, etc.)
kdePackages.yakuake # Drop-down terminal emulator
];
@@ -77,61 +80,66 @@ in
programs.spotify-player.enable = true;
services.gnome-keyring = {
# Linux-only: GNOME keyring service
services.gnome-keyring = mkIf isLinux {
enable = true;
};
# rbw vault unlock on login and resume from suspend
systemd.user.services.rbw-unlock-on-login = {
Unit = {
Description = "Unlock rbw vault at login";
After = [ "graphical-session.target" ];
# Linux-only: systemd user services for rbw vault unlock
systemd.user.services = mkIf isLinux {
# rbw vault unlock on login
rbw-unlock-on-login = {
Unit = {
Description = "Unlock rbw vault at login";
After = [ "graphical-session.target" ];
};
Service = {
Type = "oneshot";
ExecStart = "${pkgs.rbw}/bin/rbw unlock";
Environment = "RBW_AGENT=${pkgs.rbw}/bin/rbw-agent";
# KillMode = "process" prevents systemd from killing the rbw-agent daemon
# when this oneshot service completes. The agent is spawned by rbw unlock
# and needs to persist after the service exits.
KillMode = "process";
};
Install = {
WantedBy = [ "graphical-session.target" ];
};
};
Service = {
Type = "oneshot";
ExecStart = "${pkgs.rbw}/bin/rbw unlock";
Environment = "RBW_AGENT=${pkgs.rbw}/bin/rbw-agent";
# KillMode = "process" prevents systemd from killing the rbw-agent daemon
# when this oneshot service completes. The agent is spawned by rbw unlock
# and needs to persist after the service exits.
KillMode = "process";
};
Install = {
WantedBy = [ "graphical-session.target" ];
# rbw vault unlock on resume from suspend
rbw-unlock-on-resume = {
Unit = {
Description = "Unlock rbw vault after resume from suspend";
After = [ "suspend.target" ];
};
Service = {
Type = "oneshot";
ExecStart = "${pkgs.rbw}/bin/rbw unlock";
Environment = "RBW_AGENT=${pkgs.rbw}/bin/rbw-agent";
# KillMode = "process" prevents systemd from killing the rbw-agent daemon
# when this oneshot service completes. The agent is spawned by rbw unlock
# and needs to persist after the service exits.
KillMode = "process";
};
Install = {
WantedBy = [ "suspend.target" ];
};
};
};
systemd.user.services.rbw-unlock-on-resume = {
Unit = {
Description = "Unlock rbw vault after resume from suspend";
After = [ "suspend.target" ];
};
Service = {
Type = "oneshot";
ExecStart = "${pkgs.rbw}/bin/rbw unlock";
Environment = "RBW_AGENT=${pkgs.rbw}/bin/rbw-agent";
# KillMode = "process" prevents systemd from killing the rbw-agent daemon
# when this oneshot service completes. The agent is spawned by rbw unlock
# and needs to persist after the service exits.
KillMode = "process";
};
Install = {
WantedBy = [ "suspend.target" ];
};
};
# KDE environment variables for proper integration
home.sessionVariables = {
# Linux-only: KDE environment variables for proper integration
home.sessionVariables = mkIf isLinux {
QT_QPA_PLATFORMTHEME = "kde";
KDE_SESSION_VERSION = "6";
};
xdg = {
enable = true;
# Ensure desktop files are made available for discovery
desktopEntries = {}; # This creates the desktop files directory structure
mimeApps = {
enable = true;
associations.added = {
@@ -141,13 +149,14 @@ in
"x-scheme-handler/https" = "firefox.desktop";
};
defaultApplications = {
# Web browsers
# Web browsers (cross-platform)
"text/html" = "firefox.desktop";
"x-scheme-handler/http" = "firefox.desktop";
"x-scheme-handler/https" = "firefox.desktop";
"x-scheme-handler/about" = "firefox.desktop";
"x-scheme-handler/unknown" = "firefox.desktop";
} // optionalAttrs isLinux {
# Linux-only: KDE application associations
# Documents
"application/pdf" = "okular.desktop";
"text/plain" = "kate.desktop";
@@ -155,7 +164,7 @@ in
"text/x-c" = "kate.desktop";
"text/x-python" = "kate.desktop";
"application/x-shellscript" = "kate.desktop";
# Images
"image/png" = "gwenview.desktop";
"image/jpeg" = "gwenview.desktop";
@@ -164,25 +173,25 @@ in
"image/bmp" = "gwenview.desktop";
"image/tiff" = "gwenview.desktop";
"image/webp" = "gwenview.desktop";
# Archives
"application/zip" = "ark.desktop";
"application/x-tar" = "ark.desktop";
"application/x-compressed-tar" = "ark.desktop";
"application/x-7z-compressed" = "ark.desktop";
"application/x-rar" = "ark.desktop";
# Audio
"audio/mpeg" = "elisa.desktop";
"audio/mp4" = "elisa.desktop";
"audio/flac" = "elisa.desktop";
"audio/ogg" = "elisa.desktop";
"audio/wav" = "elisa.desktop";
# Email
"message/rfc822" = "kmail.desktop";
"x-scheme-handler/mailto" = "kmail.desktop";
# Calendar
"text/calendar" = "korganizer.desktop";
"application/x-vnd.akonadi.calendar.event" = "korganizer.desktop";
@@ -190,9 +199,11 @@ in
};
};
# Fix for KDE applications.menu file issue on Plasma 6
# Linux-only: Fix for KDE applications.menu file issue on Plasma 6
# KDE still looks for applications.menu but Plasma 6 renamed it to plasma-applications.menu
xdg.configFile."menus/applications.menu".source = "${pkgs.kdePackages.plasma-workspace}/etc/xdg/menus/plasma-applications.menu";
xdg.configFile."menus/applications.menu" = mkIf isLinux {
source = "${pkgs.kdePackages.plasma-workspace}/etc/xdg/menus/plasma-applications.menu";
};
# Note: modules must be imported at top-level home config
};

View File

@@ -4,6 +4,7 @@ with lib;
let
cfg = config.home.roles.email;
isLinux = pkgs.stdenv.isLinux;
in
{
options.home.roles.email = {
@@ -89,34 +90,38 @@ in
account default : proton
'';
# Systemd service for mail sync
systemd.user.services.mbsync = {
Unit = {
Description = "Mailbox synchronization service";
After = [ "network-online.target" ];
Wants = [ "network-online.target" ];
};
Service = {
Type = "oneshot";
ExecStart = "${pkgs.bash}/bin/bash -c 'mkdir -p ~/Mail && ${pkgs.isync}/bin/mbsync -a && (${pkgs.mu}/bin/mu info >/dev/null 2>&1 || ${pkgs.mu}/bin/mu init --maildir ~/Mail --personal-address=john@ogle.fyi) && ${pkgs.mu}/bin/mu index'";
Environment = "PATH=${pkgs.rbw}/bin:${pkgs.coreutils}/bin";
StandardOutput = "journal";
StandardError = "journal";
# Linux-only: Systemd service for mail sync (Darwin uses launchd instead)
systemd.user.services = mkIf isLinux {
mbsync = {
Unit = {
Description = "Mailbox synchronization service";
After = [ "network-online.target" ];
Wants = [ "network-online.target" ];
};
Service = {
Type = "oneshot";
ExecStart = "${pkgs.bash}/bin/bash -c 'mkdir -p ~/Mail && ${pkgs.isync}/bin/mbsync -a && (${pkgs.mu}/bin/mu info >/dev/null 2>&1 || ${pkgs.mu}/bin/mu init --maildir ~/Mail --personal-address=john@ogle.fyi) && ${pkgs.mu}/bin/mu index'";
Environment = "PATH=${pkgs.rbw}/bin:${pkgs.coreutils}/bin";
StandardOutput = "journal";
StandardError = "journal";
};
};
};
# Systemd timer for automatic sync
systemd.user.timers.mbsync = {
Unit = {
Description = "Mailbox synchronization timer";
};
Timer = {
OnBootSec = "2min";
OnUnitActiveSec = "5min";
Unit = "mbsync.service";
};
Install = {
WantedBy = [ "timers.target" ];
# Linux-only: Systemd timer for automatic sync
systemd.user.timers = mkIf isLinux {
mbsync = {
Unit = {
Description = "Mailbox synchronization timer";
};
Timer = {
OnBootSec = "2min";
OnUnitActiveSec = "5min";
Unit = "mbsync.service";
};
Install = {
WantedBy = [ "timers.target" ];
};
};
};
};

View File

@@ -4,13 +4,15 @@ with lib;
let
cfg = config.home.roles.kdeconnect;
isLinux = pkgs.stdenv.isLinux;
in
{
options.home.roles.kdeconnect = {
enable = mkEnableOption "Enable KDE Connect for device integration";
};
config = mkIf cfg.enable {
# KDE Connect services are Linux-only (requires D-Bus and systemd)
config = mkIf (cfg.enable && isLinux) {
services.kdeconnect = {
enable = true;
indicator = true;

View File

@@ -4,6 +4,7 @@ with lib;
let
cfg = config.home.roles.sync;
isLinux = pkgs.stdenv.isLinux;
in
{
options.home.roles.sync = {
@@ -11,9 +12,10 @@ in
};
config = mkIf cfg.enable {
home.packages = with pkgs; [
# Linux-only: syncthingtray requires system tray support
home.packages = optionals isLinux (with pkgs; [
syncthingtray
];
]);
services.syncthing = {
enable = true;