Skip to content

Set up Nix/NixOS for classroom laptops#17

Open
bottd wants to merge 1 commit intoWeAllCode:mainfrom
bottd:main
Open

Set up Nix/NixOS for classroom laptops#17
bottd wants to merge 1 commit intoWeAllCode:mainfrom
bottd:main

Conversation

@bottd
Copy link
Copy Markdown

@bottd bottd commented Mar 23, 2026

Summary

Replaces the previous scripts for macOS config with a NixOS flake configuration for the classroom laptops (Intel MacBooks).

We ran into issues getting programs to run consistently across these machines, especially because modern versions of MacOS do not support these MacBooks. With Linux we can run an up-to-date system on legacy hardware. NixOS lets us declare the entire system config in code so every laptop is identical.

Omar and I have successfully installed this configuration on the laptops that have USB-A ports. During setup we verified that Python with tkinter runs successfully in VS Code, all VS Code extensions are installed, and AI features are turned off.

Included in this PR:

NixOS system config

  • XFCE desktop

  • WeAllCode wallpaper

  • auto-login

  • Python 3

    • tkinter
    • weallcode_robot
    • rich (used in scripts)
  • VS Code

    • correct Python interpreter path set
    • AI features disabled
    • Ruff
    • Pylance
    • Prettier
  • Firefox

  • Broadcom WiFi driver

Bootable installer ISO

  • GitHub Action builds installation .iso
  • Boots into XFCE with a Classroom Setup shortcut that partitions the disk and installs NixOS

Classroom Update script

  • Desktop shortcut that rebuilds the system from the latest config on GitHub and installs weallcode-robot via pip

Renovate

  • Monthly automated dependency updates for flake inputs and GitHub Actions

Dev environment

  • direnv sets up a dev shell with linting/formatting for nix, python, and yaml.

Copy link
Copy Markdown

Copilot AI left a comment

Choose a reason for hiding this comment

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

Pull request overview

This PR migrates the classroom laptop setup from macOS bootstrap scripts to a declarative NixOS flake that can be built into both an installable system configuration and a bootable installer ISO for Intel MacBooks.

Changes:

  • Introduces a NixOS flake with modular system configuration (XFCE desktop, VS Code, Python, networking, Broadcom WiFi).
  • Adds a GitHub Actions workflow to build and publish a bootable installer ISO plus a Python-based install/update flow.
  • Adds Renovate + dev tooling (direnv + treefmt/pre-commit), and removes the legacy macOS provisioning artifacts.

Reviewed changes

Copilot reviewed 23 out of 26 changed files in this pull request and generated 6 comments.

Show a summary per file
File Description
settings.json Removes legacy macOS VS Code settings bootstrap file.
org.weallcode.logon.plist Removes legacy macOS LaunchAgent.
logon.script.sh Removes legacy macOS logon script.
install.sh Removes legacy macOS installation script.
.zshrc Removes legacy macOS zsh customization.
flake.nix Defines flake inputs, dev tooling (treefmt/pre-commit), and NixOS/ISO outputs.
flake.lock Pins flake inputs for reproducible builds.
modules/base.nix Main classroom NixOS configuration, packages, Home Manager wiring, update shortcut.
modules/disk.nix Disk partitioning layout for disko-based installs.
modules/hardware-configuration.nix Hardware/boot settings targeting Intel MacBooks (incl. Broadcom import).
modules/broadcom.nix Enables Broadcom STA driver and related kernel module settings.
modules/networkmanager.nix Enables NetworkManager and configures system-wide WiFi connection storage.
modules/nix-settings.nix Sets Nix daemon settings, substituters, and GC defaults.
modules/python.nix Provides Python environment (tkinter/rich/pip) for classroom usage and scripts.
modules/vscode.nix Home Manager VS Code config: extensions, interpreter path, AI feature disabling, editor defaults.
modules/xfce.nix XFCE desktop setup: autologin, wallpaper, panel/launcher config.
modules/iso.nix Installer ISO configuration and desktop “Classroom Setup” shortcut.
scripts/machine-setup.py Installer-side script that partitions, installs NixOS from the flake, and reboots.
scripts/update.py Classroom update script that rebuilds from GitHub flake and upgrades weallcode-robot via pip.
README.md Documents new NixOS-based install/update process and repo structure.
.envrc Enables direnv flake-based dev shell.
.gitignore Updates ignored files for direnv/pre-commit/ruff cache.
renovate.json Adds Renovate configuration for monthly dependency updates (incl. Nix).
.github/workflows/build-iso.yml Builds the installer ISO in CI and uploads it as an artifact.
.github/workflows/renovate.yml Runs Renovate on a schedule / manually.

💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.

Comment on lines +30 to +34
console.rule("Installing weallcode-robot")
subprocess.run(
["pip", "install", "--user", "--upgrade", ROBOT_PKG],
check=True,
)
Copy link

Copilot AI Mar 23, 2026

Choose a reason for hiding this comment

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

The update script shells out to pip directly and installs from an unpinned Git URL. Using pip (vs python -m pip) can pick up a different interpreter than the one configured for the system, and installing from a moving Git HEAD is a supply-chain/reproducibility risk. Consider invoking pip via the intended interpreter and pinning weallcode-robot to a tag/commit (or a published version) if you need deterministic updates.

Copilot uses AI. Check for mistakes.
"/mnt",
"--flake",
FLAKE_REF,
"--no-root-password",
Copy link

Copilot AI Mar 23, 2026

Choose a reason for hiding this comment

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

nixos-install --no-root-password can result in a system where the root account has an empty password (depending on installer defaults), which is risky on shared devices. Consider explicitly locking the root account after install and relying on a dedicated admin user, or setting a root password via a hashed secret if root login is needed.

Suggested change
"--no-root-password",

Copilot uses AI. Check for mistakes.
Comment on lines +1 to +6
{ pkgs, ... }:
let
python = pkgs.python3.withPackages (ps: [ ps.tkinter ps.rich ps.pip ]);
in
{
environment.systemPackages = [ python ];
Copy link

Copilot AI Mar 23, 2026

Choose a reason for hiding this comment

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

The PR description lists weallcode_robot as part of the base Python setup, but this module only installs tkinter, rich, and pip. If weallcode-robot is expected to be available immediately after installation (before running the update shortcut), consider adding it here (via Nix or by running the pip install as part of the install flow) or updating the PR description to match the actual behavior.

Copilot uses AI. Check for mistakes.
Comment on lines +17 to +21
users.users.weallcode = {
isNormalUser = true;
extraGroups = [ "wheel" "networkmanager" "audio" "video" ];
initialPassword = "Coder4life!";
};
Copy link

Copilot AI Mar 23, 2026

Choose a reason for hiding this comment

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

users.users.weallcode.initialPassword hard-codes a known plaintext password in the repo. Since the user is also in wheel, anyone with access can gain admin privileges (and the password is effectively public). Consider removing the plaintext password from the flake (e.g., set a locked password and rely on auto-login + passwordless sudo for only specific commands, or use a hashed password sourced from a non-committed secret).

Copilot uses AI. Check for mistakes.
Comment on lines +60 to +63
initialPassword = "Coder4life!";
};

security.sudo.wheelNeedsPassword = false;
Copy link

Copilot AI Mar 23, 2026

Choose a reason for hiding this comment

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

The installer user is configured with a plaintext initialPassword and passwordless sudo for the entire wheel group. Combined with auto-login, this makes the live environment trivially root-accessible. If this ISO could be used outside a controlled setup flow, consider locking the account password and/or narrowing passwordless sudo to just the commands needed for installation (e.g., disko, nixos-install, reboot).

Suggested change
initialPassword = "Coder4life!";
};
security.sudo.wheelNeedsPassword = false;
# Lock the password to avoid a reusable plaintext installer password.
# Auto-login via the display manager still works without a usable password hash.
initialHashedPassword = "!";
};
security.sudo = {
# Require a password for general wheel usage.
wheelNeedsPassword = true;
# Allow the installer user to run only the necessary installation commands
# without a password, rather than granting full passwordless root access.
extraRules = [
{
users = [ "nixos" ];
commands = [
{ command = "/run/current-system/sw/bin/disko"; options = [ "NOPASSWD" ]; }
{ command = "/run/current-system/sw/bin/nixos-install"; options = [ "NOPASSWD" ]; }
{ command = "/run/current-system/sw/bin/reboot"; options = [ "NOPASSWD" ]; }
];
}
];
};

Copilot uses AI. Check for mistakes.
Comment on lines +13 to +16
"editor.wordWrap" = "on";
"editor.defaultFormatter" = "charliermarsh.ruff";
"editor.minimap.enabled" = false;
"editor.formatOnSave" = true;
Copy link

Copilot AI Mar 23, 2026

Choose a reason for hiding this comment

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

editor.defaultFormatter is set globally to the Ruff extension. That will apply to non-Python files too, which can break formatting (and also prevents Prettier from being used where intended). Consider scoping Ruff as the default formatter only under [python] and setting Prettier for relevant filetypes (or omitting a global default).

Copilot uses AI. Check for mistakes.
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.

2 participants