Skip to content

feat(apply): add image build option#127

Merged
water-sucks merged 3 commits intonix-community:mainfrom
water-sucks:add-image-build-option
Nov 6, 2025
Merged

feat(apply): add image build option#127
water-sucks merged 3 commits intonix-community:mainfrom
water-sucks:add-image-build-option

Conversation

@water-sucks
Copy link
Copy Markdown
Collaborator

@water-sucks water-sucks commented Nov 6, 2025

Description

nixos-rebuild.sh supports building images for various platforms, like standalone installer ISOs, Proxmox images, EC2 images, and the like through its config.system.build.images attribute.

This PR adds support for building those images, similar to the way that it can already build NixOS VMs.

Closes #113.

Summary by CodeRabbit

  • New Features

    • Add --image / -i to build disk-image variants and list available variants when none or invalid.
    • Build output now shows the produced image path or VM/script location with clearer messages.
  • Bug Fixes

    • Validate requested images before building; requesting "_list" prints available images and exits.
    • Restrict root re-execution to local system activation scenarios.
  • Refactor

    • Reorganized Nix/build option handling and build-path selection (no user-facing behavior changes).

@coderabbitai
Copy link
Copy Markdown

coderabbitai bot commented Nov 6, 2025

Walkthrough

Adds image build support to the apply command, introduces a polymorphic BuildType interface with SystemBuild/VMBuild/ImageBuild, updates CLI options and apply flow to enumerate/validate image variants and resolve image paths, and adjusts activation/re-exec and dry-run behavior per build type.

Changes

Cohort / File(s) Summary
Build type polymorphism
internal/configuration/configuration.go, internal/configuration/flake.go, internal/configuration/legacy.go
Replace SystemBuildType enum with BuildType interface; add SystemBuild, VMBuild, ImageBuild structs implementing BuildAttr(); update BuildSystem signatures and internal dispatch to accept BuildType
Apply command enhancements
cmd/apply/apply.go
Add --image handling (including hidden _list flow), integrate ImageBuild into build flow, add internal helpers to enumerate/validate image variants (getAvailableImageAttrs) and resolve image paths (getImageName), update dry-run/activation/re-exec logic and final messaging per build type
CLI options refactor
internal/cmd/opts/opts.go
Add BuildImage string to ApplyOpts; extract nested Nix option groups into public ApplyNixOpts and InstallNixOpts and replace inline structs
Install callsite
cmd/install/install.go
Call BuildSystem with an empty SystemBuild{} (polymorphic BuildType) instead of previous enum constant
Manual update
doc/man/nixos-cli-apply.1.scd
Add -i, --image <VARIANT> option documentation and describe _list behavior

Sequence Diagram(s)

sequenceDiagram
    participant User
    participant CLI as "nixos apply"
    participant Config as "configuration"
    participant Nix as "nix evaluator"

    User->>CLI: run with --image <variant|_list>
    activate CLI
    CLI->>CLI: parse flags -> set BuildImage ("_list" or variant)
    alt BuildImage == "_list"
        CLI->>Config: getAvailableImageAttrs()
        Config->>Nix: evaluate image attribute list
        Nix-->>Config: list of variants
        Config-->>CLI: variants
        CLI-->>User: print variants and exit
    else BuildImage == variant
        CLI->>Config: BuildSystem(ImageBuild{Variant})
        Config->>Nix: resolve image attribute / path
        alt image resolved
            Nix-->>Config: image path
            Config-->>CLI: image path
            CLI->>CLI: perform build/instantiate (apply rules, dry-run, activation)
            CLI-->>User: print image path / result
        else not found
            Config->>Nix: getAvailableImageAttrs()
            Nix-->>Config: list of variants
            Config-->>CLI: variants
            CLI-->>User: print variants and fail
        end
    end
    deactivate CLI
Loading

Estimated code review effort

🎯 4 (Complex) | ⏱️ ~45 minutes

  • Focus review on:
    • internal/configuration/configuration.go — correctness of BuildAttr() implementations and public signature changes.
    • cmd/apply/apply.go — image enumeration/validation, _list behavior, getImageName/getAvailableImageAttrs, and conditional re-exec/activation/dry-run logic.
    • internal/configuration/legacy.go and internal/configuration/flake.go — attribute selection for ImageBuild vs other builds and -k activation flag decisions.
    • internal/cmd/opts/opts.go — public API shape changes for option structs and added BuildImage field.

Pre-merge checks and finishing touches

❌ Failed checks (1 warning)
Check name Status Explanation Resolution
Docstring Coverage ⚠️ Warning Docstring coverage is 0.00% which is insufficient. The required threshold is 80.00%. You can run @coderabbitai generate docstrings to improve docstring coverage.
✅ Passed checks (4 passed)
Check name Status Explanation
Description Check ✅ Passed Check skipped - CodeRabbit’s high-level summary is enabled.
Title check ✅ Passed The title clearly and specifically describes the main change—adding an image build option to the apply command, matching the primary focus of the changeset.
Linked Issues check ✅ Passed All requirements from issue #113 are met: --image flag added with variant selection, listing on empty value, integration with config.system.build.images, and polymorphic build type support.
Out of Scope Changes check ✅ Passed All changes are scoped to implementing image-building support; the opts.go refactoring separating ApplyNixOpts and InstallNixOpts is a supporting structural change necessary for the feature.
✨ Finishing touches
  • 📝 Generate docstrings
🧪 Generate unit tests (beta)
  • Create PR with unit tests
  • Post copyable unit tests in a comment

📜 Recent review details

Configuration used: CodeRabbit UI

Review profile: CHILL

Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between 1e8a782 and 2b3816b.

📒 Files selected for processing (4)
  • cmd/apply/apply.go (9 hunks)
  • internal/cmd/opts/opts.go (3 hunks)
  • internal/configuration/configuration.go (2 hunks)
  • internal/configuration/legacy.go (5 hunks)
🧰 Additional context used
🧬 Code graph analysis (3)
internal/configuration/configuration.go (2)
internal/build/build.go (1)
  • Flake (32-34)
internal/build/vars/vars.go (1)
  • Flake (9-9)
internal/configuration/legacy.go (1)
internal/configuration/configuration.go (4)
  • BuildType (71-73)
  • SystemBuildOptions (13-26)
  • ImageBuild (99-101)
  • SystemBuild (75-77)
cmd/apply/apply.go (6)
internal/configuration/configuration.go (6)
  • BuildType (71-73)
  • VMBuild (87-89)
  • ImageBuild (99-101)
  • SystemBuild (75-77)
  • Configuration (28-32)
  • AttributeEvaluationError (34-37)
internal/utils/utils.go (1)
  • ExecAsRoot (15-26)
internal/system/runner.go (2)
  • Command (16-23)
  • NewCommand (25-34)
internal/system/system.go (1)
  • System (9-14)
internal/cmd/opts/opts.go (1)
  • ApplyNixOpts (48-76)
internal/cmd/nixopts/convert.go (1)
  • NixOptionsToArgsListByCategory (91-132)
⏰ 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/Test

Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out.

❤️ Share

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

Copy link
Copy Markdown

@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

Caution

Some comments are outside the diff and can’t be posted inline due to platform limitations.

⚠️ Outside diff range comments (1)
internal/configuration/legacy.go (1)

187-205: Fix remote image builds for legacy configs.

When --build-host is used with --image, we call nix-instantiate -A images.<variant>, but legacy images actually live under config.system.build.images.<variant>. That mismatch makes every remote image build fail before copying the drv.

$ nix-instantiate … -A images.amazon
error: attribute 'images' missing

Please mirror the local buildLocalSystem logic and prepend config.system.build. for *ImageBuild here.

-	instantiateArgv := []string{"nix-instantiate", "<nixpkgs/nixos>", "-A", buildType.BuildAttr()}
+	instantiateArgv := []string{"nix-instantiate", "<nixpkgs/nixos>", "-A"}
+	if _, ok := buildType.(*ImageBuild); ok {
+		instantiateArgv = append(instantiateArgv, fmt.Sprintf("config.system.build.%s", buildType.BuildAttr()))
+	} else {
+		instantiateArgv = append(instantiateArgv, buildType.BuildAttr())
+	}
📜 Review details

Configuration used: CodeRabbit UI

Review profile: CHILL

Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between ee29017 and 1c89054.

📒 Files selected for processing (7)
  • cmd/apply/apply.go (9 hunks)
  • cmd/install/install.go (1 hunks)
  • doc/man/nixos-cli-apply.1.scd (1 hunks)
  • internal/cmd/opts/opts.go (3 hunks)
  • internal/configuration/configuration.go (2 hunks)
  • internal/configuration/flake.go (3 hunks)
  • internal/configuration/legacy.go (4 hunks)
🧰 Additional context used
🧬 Code graph analysis (5)
internal/configuration/flake.go (3)
internal/system/local.go (1)
  • LocalSystem (16-18)
internal/configuration/configuration.go (2)
  • BuildType (71-73)
  • SystemBuildOptions (13-26)
internal/system/ssh.go (1)
  • SSHSystem (30-39)
cmd/install/install.go (1)
internal/configuration/configuration.go (1)
  • SystemBuild (75-77)
cmd/apply/apply.go (9)
internal/configuration/configuration.go (6)
  • BuildType (71-73)
  • VMBuild (87-89)
  • ImageBuild (99-101)
  • SystemBuild (75-77)
  • Configuration (28-32)
  • AttributeEvaluationError (34-37)
internal/utils/utils.go (1)
  • ExecAsRoot (15-26)
internal/system/runner.go (2)
  • Command (16-23)
  • NewCommand (25-34)
internal/system/system.go (1)
  • System (9-14)
internal/cmd/opts/opts.go (1)
  • ApplyNixOpts (48-76)
internal/configuration/flake.go (1)
  • FlakeRef (17-23)
internal/cmd/nixopts/convert.go (1)
  • NixOptionsToArgsListByCategory (91-132)
internal/configuration/legacy.go (1)
  • LegacyConfiguration (16-22)
internal/logger/logger.go (1)
  • Logger (12-27)
internal/configuration/configuration.go (2)
internal/build/build.go (1)
  • Flake (32-34)
internal/build/vars/vars.go (1)
  • Flake (9-9)
internal/configuration/legacy.go (3)
internal/system/local.go (1)
  • LocalSystem (16-18)
internal/configuration/configuration.go (4)
  • BuildType (71-73)
  • SystemBuildOptions (13-26)
  • ImageBuild (99-101)
  • SystemBuild (75-77)
internal/system/ssh.go (1)
  • SSHSystem (30-39)
⏰ 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/Test

@water-sucks water-sucks force-pushed the add-image-build-option branch from 1c89054 to 1e8a782 Compare November 6, 2025 09:08
Copy link
Copy Markdown

@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

🧹 Nitpick comments (2)
cmd/apply/apply.go (2)

310-332: Consider using slices.Contains for existence check.

The validation logic is correct, but slices.Contains is more idiomatic than slices.Index for checking existence.

Apply this diff:

-		if slices.Index(images, imgBuild.Variant) < 0 {
+		if !slices.Contains(images, imgBuild.Variant) {
 			err := fmt.Errorf("image type '%s' is not available", imgBuild.Variant)
 			log.Error(err)
 			log.Info("pass an empty string to `--image` to get a list of available images")
 			return err
 		}

334-339: Add build step message for ImageBuild.

The ImageBuild case lacks a corresponding build step message, which could be confusing for users.

Apply this diff:

 	switch buildType.(type) {
 	case *configuration.SystemBuild:
 		log.Step("Building configuration...")
 	case *configuration.VMBuild:
 		log.Step("Building VM...")
+	case *configuration.ImageBuild:
+		log.Step("Building image...")
 	}
📜 Review details

Configuration used: CodeRabbit UI

Review profile: CHILL

Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between 1c89054 and 1e8a782.

📒 Files selected for processing (4)
  • cmd/apply/apply.go (9 hunks)
  • internal/cmd/opts/opts.go (3 hunks)
  • internal/configuration/configuration.go (2 hunks)
  • internal/configuration/legacy.go (5 hunks)
🚧 Files skipped from review as they are similar to previous changes (1)
  • internal/configuration/configuration.go
🧰 Additional context used
🧬 Code graph analysis (2)
cmd/apply/apply.go (8)
internal/configuration/configuration.go (6)
  • BuildType (71-73)
  • VMBuild (87-89)
  • ImageBuild (99-101)
  • SystemBuild (75-77)
  • Configuration (28-32)
  • AttributeEvaluationError (34-37)
internal/utils/utils.go (1)
  • ExecAsRoot (15-26)
internal/system/runner.go (2)
  • Command (16-23)
  • NewCommand (25-34)
internal/system/system.go (1)
  • System (9-14)
internal/cmd/opts/opts.go (1)
  • ApplyNixOpts (48-76)
internal/configuration/flake.go (1)
  • FlakeRef (17-23)
internal/cmd/nixopts/convert.go (1)
  • NixOptionsToArgsListByCategory (91-132)
internal/configuration/legacy.go (1)
  • LegacyConfiguration (16-22)
internal/configuration/legacy.go (1)
internal/configuration/configuration.go (4)
  • BuildType (71-73)
  • SystemBuildOptions (13-26)
  • ImageBuild (99-101)
  • SystemBuild (75-77)
⏰ 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/Test
🔇 Additional comments (14)
internal/cmd/opts/opts.go (2)

32-32: LGTM!

The BuildImage field addition correctly supports the new image-building workflow, allowing users to specify image variants via the --image flag.


45-76: LGTM!

Extracting the inline NixOptions struct into a public ApplyNixOpts type improves code organization and reusability. All fields and nixCategory tags are preserved correctly.

cmd/apply/apply.go (9)

74-79: LGTM!

The sentinel value _list correctly triggers image listing when --image is provided without a value, enabling the "list available images and exit" workflow described in the PR objectives.


103-103: LGTM!

The flag definition properly integrates with the sentinel value logic and provides clear help text.


158-158: LGTM!

Correctly prevents conflicting build types by marking --vm, --vm-with-bootloader, and --image as mutually exclusive.


212-219: LGTM!

The BuildType construction correctly implements polymorphic dispatch, with proper logic for determining whether SystemBuild should activate based on the NoActivate and NoBoot flags.


225-235: LGTM!

Restricting root re-execution to local SystemBuild with activation is a good optimization that avoids unnecessary privilege escalation for VM and image builds.


386-391: LGTM!

The dry-run logic now correctly preserves --dry for non-activating builds (VM, image) while ensuring that activating system builds always perform a real build. This addresses the concern raised in the previous review.


423-455: LGTM with a minor note.

The finalization logic correctly handles each build type with appropriate messaging and early returns to prevent unwanted activation. The fallback messaging when getImageName fails is a good user experience touch.

Note: If imagePath returned by getImageName could be an absolute path, the filepath.Join at line 430 may produce unexpected results. Based on the implementation (line 756), it returns a trimmed string from Nix evaluation, which should be relative, so this is likely not an issue.


665-711: LGTM!

The getAvailableImageAttrs helper correctly handles both Flake and Legacy configurations, with proper error handling and JSON decoding. The use of category-specific Nix options (lock for Flake, build for Legacy) aligns with the established pattern in the codebase.


713-757: LGTM!

The getImageName helper correctly resolves image file paths for both configuration types. The command logging (line 746) aids debugging, and the use of --raw ensures clean string output. The imgName parameter is safely used because it's validated against available images before this function is called.

internal/configuration/legacy.go (3)

116-137: LGTM!

The buildLocalSystem signature update and attribute construction correctly handle the ImageBuild case with the config.system.build. prefix, while other build types use the attribute directly. The -k flag logic properly mirrors nixos-rebuild behavior by adding the flag for all non-activating builds.


229-231: LGTM!

The -k flag logic in the realise step correctly mirrors the pattern in buildLocalSystem, ensuring consistent behavior for non-activating builds.


244-257: LGTM!

The BuildSystem signature update correctly integrates with the polymorphic BuildType interface, maintaining consistent dispatch logic to the appropriate internal build functions.

@water-sucks water-sucks force-pushed the add-image-build-option branch from 1e8a782 to 2b3816b Compare November 6, 2025 09:17
@water-sucks water-sucks merged commit eecf026 into nix-community:main Nov 6, 2025
1 of 2 checks passed
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.

apply: consider adding an --image option

1 participant