Skip to content
Open
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
2 changes: 2 additions & 0 deletions cmd/run.go
Original file line number Diff line number Diff line change
Expand Up @@ -62,6 +62,8 @@ var runCmd = &cobra.Command{
}
case "nix":
executor, err = executorPkg.NewNixOSNix()
case "system-manager":
executor, err = executorPkg.NewSystemManagerFlake()
}
if err != nil {
logrus.Errorf("Failed to create the executor: %s", err)
Expand Down
1 change: 1 addition & 0 deletions flake.nix
Original file line number Diff line number Diff line change
Expand Up @@ -60,6 +60,7 @@
});
nixosModules.comin = nixpkgs.lib.modules.importApply ./nix/module.nix { inherit self; };
darwinModules.comin = nixpkgs.lib.modules.importApply ./nix/darwin-module.nix { inherit self; };
systemManagerModules.comin = nixpkgs.lib.modules.importApply ./nix/system-manager-module.nix { inherit self; };
devShells = forAllSystems (
system:
let
Expand Down
2 changes: 1 addition & 1 deletion internal/config/config.go
Original file line number Diff line number Diff line change
Expand Up @@ -65,7 +65,7 @@ func Read(path string) (config types.Configuration, err error) {
if config.RepositorySubdir == "" {
config.RepositorySubdir = "."
}
supportedRepositoryTypes := []string{"flake", "nix"}
supportedRepositoryTypes := []string{"flake", "nix", "system-manager"}
if !slices.Contains(supportedRepositoryTypes, config.RepositoryType) {
return config, fmt.Errorf("config: repository type is '%s' while it be one of '%s'", config.RepositoryType, supportedRepositoryTypes)
}
Expand Down
6 changes: 6 additions & 0 deletions internal/executor/executor.go
Original file line number Diff line number Diff line change
Expand Up @@ -41,3 +41,9 @@ func NewNixOSNix() (e Executor, err error) {
e, err = NewNixExecutor()
return
}

func NewSystemManagerFlake() (e Executor, err error) {
logrus.Info("executor: creating a system-manager flake executor")
e, err = NewSystemManagerFlakeExecutor()
return
}
130 changes: 130 additions & 0 deletions internal/executor/system_manager_flake.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,130 @@
package executor

import (
"bytes"
"context"
"fmt"
"os"
"os/exec"
"path/filepath"

"github.com/nlewo/comin/internal/utils"
"github.com/sirupsen/logrus"
)

const (
systemManagerProfileDir = "/nix/var/nix/profiles/system-manager-profiles"
systemManagerProfileName = "system-manager"
)

type SystemManagerFlake struct{}

func NewSystemManagerFlakeExecutor() (*SystemManagerFlake, error) {
return &SystemManagerFlake{}, nil
}

func (s *SystemManagerFlake) ReadMachineId() (string, error) {
return utils.ReadMachineIdLinux()
}

func (s *SystemManagerFlake) NeedToReboot(outPath, operation string) bool {
return false
}

func (s *SystemManagerFlake) IsStorePathExist(storePath string) bool {
return isStorePathExist(storePath)
}

func (s *SystemManagerFlake) Eval(ctx context.Context, repositoryPath, repositorySubdir, commitId, systemAttr, hostname string, submodules bool) (drvPath string, outPath string, machineId string, err error) {
flakeUrl := fmt.Sprintf("git+file://%s?dir=%s&rev=%s", repositoryPath, repositorySubdir, commitId)
if submodules {
flakeUrl += "&submodules=1"
}
drvPath, outPath, err = showDerivationSystemManager(ctx, flakeUrl, hostname)
if err != nil {
return
}
// system-manager does not have a services.comin module, so machineId is not available
machineId = ""
return
}

func (s *SystemManagerFlake) Build(ctx context.Context, drvPath string) (err error) {
return buildWithFlake(ctx, drvPath)
}

func (s *SystemManagerFlake) Deploy(ctx context.Context, outPath, operation string, profilePaths []string) (needToRestartComin bool, profilePath string, err error) {
return deploySystemManager(ctx, outPath, operation)
}

func showDerivationSystemManager(ctx context.Context, flakeUrl, hostname string) (drvPath string, outPath string, err error) {
// system-manager's makeSystemConfig returns the toplevel derivation directly,
// so we evaluate systemConfigs."<hostname>" without any .config.system.build.toplevel suffix
installable := fmt.Sprintf("%s#systemConfigs.\"%s\"", flakeUrl, hostname)
args := []string{
"derivation",
"show",
installable,
"-L",
"--show-trace",
}
var stdout bytes.Buffer
err = runNixFlakeCommand(ctx, args, &stdout, os.Stderr)
if err != nil {
return
}
return parseDerivationWithFlake(stdout)
}

func deploySystemManager(ctx context.Context, outPath, operation string) (needToRestartComin bool, profilePath string, err error) {
beforeCominUnitFileHash := cominUnitFileHashLinux()

enginePath := filepath.Join(outPath, "bin", "system-manager-engine")

dryRun := operation == "dry-run"
if dryRun {
logrus.Infof("system-manager: dry-run enabled: register and activate have not been executed")
return
}

// Register: creates nix profile generation (skip for "test" operation)
if operation != "test" {
logrus.Infof("system-manager: running '%s register --store-path %s'", enginePath, outPath)
cmd := exec.CommandContext(ctx, enginePath, "register", "--store-path", outPath)
cmd.Stdout = os.Stdout
cmd.Stderr = os.Stderr
if err = cmd.Run(); err != nil {
err = fmt.Errorf("system-manager register failed: %w", err)
return
}
logrus.Infof("system-manager: register succeeded")
}

// Activate: apply etc files and start services
logrus.Infof("system-manager: running '%s activate --store-path %s'", enginePath, outPath)
cmd := exec.CommandContext(ctx, enginePath, "activate", "--store-path", outPath)
cmd.Stdout = os.Stdout
cmd.Stderr = os.Stderr
if err = cmd.Run(); err != nil {
err = fmt.Errorf("system-manager activate failed: %w", err)
return
}
logrus.Infof("system-manager: activate succeeded")

// Read the profile path for GC root tracking
profileLink := filepath.Join(systemManagerProfileDir, systemManagerProfileName)
if dst, readErr := os.Readlink(profileLink); readErr == nil {
profilePath = filepath.Join(systemManagerProfileDir, dst)
logrus.Infof("system-manager: current profile path is %s", profilePath)
} else {
logrus.Warnf("system-manager: could not read profile symlink %s: %s", profileLink, readErr)
}

afterCominUnitFileHash := cominUnitFileHashLinux()
if beforeCominUnitFileHash != afterCominUnitFileHash {
needToRestartComin = true
}

logrus.Infof("system-manager: deployment ended")
return
}
Loading