1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
|
package main
import (
"fmt"
"os"
"os/exec"
"strings"
)
const flakeRef = "/home/philip/kot/Profpatsch"
func main() {
opts := parseArgs(os.Args[1:])
if opts.remoteBuild {
deployRemoteBuild(opts)
} else {
deployLocalBuild(opts)
}
}
type options struct {
remoteBuild bool
machine string
// profilePath is non-empty when --profile=<path> is given.
// The nix evaluation will be profiled and the flamegraph written there.
profilePath string
}
func parseArgs(args []string) options {
var opts options
for _, a := range args {
switch {
case a == "--remote-build":
opts.remoteBuild = true
case strings.HasPrefix(a, "--profile="):
opts.profilePath = strings.TrimPrefix(a, "--profile=")
case a == "--profile":
// bare --profile without a path: use a default filename
opts.profilePath = "nix-eval.profile"
default:
opts.machine = a
}
}
if opts.machine == "" {
fmt.Fprintln(os.Stderr, "usage: deploy [--remote-build] [--profile[=<path>]] <machine>")
os.Exit(1)
}
return opts
}
// evalProfileArgs returns the extra nix options to enable flamegraph profiling,
// or nil when profiling is not requested.
func evalProfileArgs(opts options) []string {
if opts.profilePath == "" {
return nil
}
return []string{
"--option", "eval-profiler", "flamegraph",
"--option", "eval-profile-file", opts.profilePath,
}
}
// cmd runs a command with inherited stdin/stdout/stderr, printing it first.
// Exits with a non-zero status on failure.
func cmd(args ...string) {
fmt.Fprintln(os.Stderr, "$", strings.Join(args, " "))
c := exec.Command(args[0], args[1:]...)
c.Stdin = os.Stdin
c.Stdout = os.Stdout
c.Stderr = os.Stderr
if err := c.Run(); err != nil {
fmt.Fprintf(os.Stderr, "deploy: %v\n", err)
os.Exit(1)
}
}
// cmdOutput runs a command, prints it, and returns trimmed stdout.
// Exits with a non-zero status on failure.
func cmdOutput(args ...string) string {
fmt.Fprintln(os.Stderr, "$", strings.Join(args, " "))
c := exec.Command(args[0], args[1:]...)
c.Stderr = os.Stderr
out, err := c.Output()
if err != nil {
fmt.Fprintf(os.Stderr, "deploy: %v\n", err)
os.Exit(1)
}
return strings.TrimSpace(string(out))
}
func toplevelAttr(machine string) string {
return flakeRef + "#nixosConfigurations." + machine + ".config.system.build.toplevel"
}
// deployLocalBuild builds the system closure locally, copies it to the
// remote, then activates via nixos-rebuild switch.
func deployLocalBuild(opts options) {
nixBuildArgs := append(
[]string{"nix", "build", "--no-link", "--print-out-paths"},
evalProfileArgs(opts)...,
)
nixBuildArgs = append(nixBuildArgs, toplevelAttr(opts.machine))
outPath := cmdOutput(nixBuildArgs...)
cmd(
"nix", "copy",
"--to", "ssh://root@"+opts.machine,
"--substitute-on-destination",
outPath,
)
cmd(
"ssh", "root@"+opts.machine,
"nix-env", "--profile", "/nix/var/nix/profiles/system", "--set", outPath,
)
cmd(
"ssh", "root@"+opts.machine,
outPath+"/bin/switch-to-configuration", "switch",
)
if opts.profilePath != "" {
fmt.Fprintf(os.Stderr, "eval profile written to: %s\n", opts.profilePath)
}
}
// deployRemoteBuild evaluates the derivation locally, copies the .drv to the
// remote, builds there, then activates directly via switch-to-configuration.
func deployRemoteBuild(opts options) {
nixPathInfoArgs := append(
[]string{"nix", "path-info", "--derivation"},
evalProfileArgs(opts)...,
)
nixPathInfoArgs = append(nixPathInfoArgs, toplevelAttr(opts.machine))
drvPath := cmdOutput(nixPathInfoArgs...)
cmd(
"nix", "copy",
"--derivation",
"--to", "ssh://root@"+opts.machine,
drvPath,
)
outPath := cmdOutput(
"ssh", "root@"+opts.machine,
"nix", "build", "--no-link", "--print-out-paths", drvPath+"^*",
)
cmd(
"ssh", "root@"+opts.machine,
"nix-env", "--profile", "/nix/var/nix/profiles/system", "--set", outPath,
)
cmd(
"ssh", "root@"+opts.machine,
outPath+"/bin/switch-to-configuration", "switch",
)
if opts.profilePath != "" {
fmt.Fprintf(os.Stderr, "eval profile written to: %s\n", opts.profilePath)
}
}
|