confctl 1.0.0 → 2.0.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: 869735b23809f8bef14f7e1e39b57e7bcaf4a90a37c7e5f4c03064a526cc1c46
4
- data.tar.gz: 1badf4f41f9183cbc5c716d436d89b3ccae96519e2f37bf0b67c4ffe7fde89e7
3
+ metadata.gz: 602260a8486eef31801f7fcf2221f09164e4443e66ea94d6fe534f8f68c56fc2
4
+ data.tar.gz: 24b4b5dabe240f2e93e0a9784f9eda90bbf52cec2a82fab8e5c5644e323be8da
5
5
  SHA512:
6
- metadata.gz: 889dc492d0996711bff26a442532cb955dc5fcf5f25c281e25475b7a9e657f64270cc6c839e81b4a3880ab250c50e7a817c30bbfc6c8a37e0b5d7fb755bad212
7
- data.tar.gz: b894f7b1720684ad93af743e8fc55f221876c7665d7302682f8354865fdc9c54f17acb990d088981d6638d908624e4687e412dda141eede57556f03bf7f741d7
6
+ metadata.gz: 87f22fe62a9354b20e711fff429f0a34f6961f9c08489bd42f97ff1d8f9f350bc281e7eca069aecc25081ae71f2ea73f803aa06a0dc4c3d244adbfe10dbbaa86
7
+ data.tar.gz: 8f44626b3b0e9a97b37171cbc58e94a15b04d87f448110ff211bfa5bc34477d1ce9a0d9f344b19cab93ed8d23f657e44fab3d14c8bad71e62b5d65ea2d3d3d26
data/.editorconfig CHANGED
@@ -5,7 +5,7 @@ root = true
5
5
  charset = utf-8
6
6
  end_of_line = lf
7
7
 
8
- [*.{rb,erb,nix}]
8
+ [*.{rb,erb,md,nix}]
9
9
  indent_size = 2
10
10
  indent_style = space
11
11
  trim_trailing_whitespace = true
data/.gitignore CHANGED
@@ -5,4 +5,5 @@ man/*.html
5
5
  man/**/*.html
6
6
  man/man?/*.?
7
7
  html_doc
8
+ .gems
8
9
  .yardoc
data/.rubocop.yml CHANGED
@@ -1,6 +1,7 @@
1
1
  inherit_from: .rubocop_todo.yml
2
2
 
3
3
  AllCops:
4
+ TargetRubyVersion: 3.1.0
4
5
  NewCops: enable
5
6
  Exclude:
6
7
  - '*.rb'
data/CHANGELOG.md CHANGED
@@ -1,2 +1,19 @@
1
- # Sat Feb 17 2023 -- version 1.0.0
1
+ # Sun Nov 17 2024 -- version 2.0.0
2
+ - Distinguish machine `config` and `metaConfig` (breaking change)
3
+ - Support for machine carriers and netboot servers
4
+ - Optimized git fetch calls when updating software pins
5
+ - Added option `--cores` that is passed to nix-build
6
+ - Added RuboCop
7
+ - Bug fixes
8
+
9
+ ## Transition to `metaConfig`
10
+ The use of `config` has been ambiguous, it could either mean machine
11
+ configuration, i.e. the result of all configured NixOS/vpsAdminOS options,
12
+ or it could mean machine metadata from `module.nix`. Machine metadata
13
+ is now accessible as `metaConfig`.
14
+
15
+ - `confLib.findConfig` has been renamed to `confLib.findMetaConfig`
16
+ - `confLib.confLib.getClusterMachines` returns a list of machines with `metaConfig` attribute
17
+
18
+ # Sat Feb 17 2024 -- version 1.0.0
2
19
  - Initial release
data/README.md CHANGED
@@ -13,6 +13,7 @@ machines.
13
13
  configurations)
14
14
  * Query machine state, view changelogs and diffs
15
15
  * Run health checks
16
+ * Support for creating netboot servers, see [docs/carrier.md](docs/carrier.md)
16
17
 
17
18
  ## Requirements
18
19
 
@@ -40,19 +41,12 @@ stored:
40
41
  ```
41
42
  mkdir cluster-configuration
42
43
  ```
43
- 3. Prepare `shell.nix` in the new directory:
44
- - Create a `shell.nix` and import the same file from confctl:
44
+ 3. Create `shell.nix` and import the same file from confctl:
45
45
  ```
46
46
  cd cluster-configuration
47
47
  cat > shell.nix <<EOF
48
48
  import ../confctl/shell.nix
49
49
  EOF
50
- ```
51
-
52
- - Alternatively, you can symlink `shell.nix` from the confctl repository:
53
- ```
54
- cd cluster-configuration
55
- ln -s ../confctl/shell.nix shell.nix
56
50
  ```
57
51
 
58
52
  4. Enter the `nix-shell`. This will make confctl available and install its
@@ -489,7 +483,7 @@ Then you can use it in machine module as:
489
483
  Note that these modules are self-contained. They are not evaluated with the full
490
484
  set of NixOS modules. You have to import modules that you need.
491
485
 
492
- ## User-defined confctl commands
486
+ ## User-defined confctl commands
493
487
  User-defined Ruby scripts can be placed in directory `scripts`. Each script
494
488
  should create a subclass of `ConfCtl::UserScript` and call class-method `register`.
495
489
  Scripts can define their own `confctl` subcommands.
data/confctl.gemspec CHANGED
@@ -15,19 +15,19 @@ Gem::Specification.new do |s|
15
15
  s.executables = s.files.grep(%r{^bin/}) { |f| File.basename(f) }
16
16
  s.license = 'GPL-3.0-only'
17
17
 
18
- s.required_ruby_version = ">= #{File.read('.ruby-version').strip}"
18
+ s.required_ruby_version = '>= 3.1.0'
19
19
 
20
- s.add_runtime_dependency 'curses'
21
- s.add_runtime_dependency 'gli', '~> 2.21.0'
22
- s.add_runtime_dependency 'json'
23
- s.add_runtime_dependency 'md2man'
24
- s.add_runtime_dependency 'rainbow', '~> 3.1.1'
25
- s.add_runtime_dependency 'rake'
26
- s.add_runtime_dependency 'require_all', '~> 2.0.0'
27
- s.add_runtime_dependency 'tty-command', '~> 0.10.1'
28
- s.add_runtime_dependency 'tty-cursor', '~> 0.7.1'
29
- s.add_runtime_dependency 'tty-pager', '~> 0.14.0'
30
- s.add_runtime_dependency 'tty-progressbar', '~> 0.18.2'
31
- s.add_runtime_dependency 'tty-spinner', '~> 0.9.3'
32
- s.add_runtime_dependency 'vpsfree-client', '~> 0.18.0'
20
+ s.add_dependency 'curses'
21
+ s.add_dependency 'gli', '~> 2.22.0'
22
+ s.add_dependency 'json'
23
+ s.add_dependency 'md2man'
24
+ s.add_dependency 'rainbow', '~> 3.1.1'
25
+ s.add_dependency 'rake'
26
+ s.add_dependency 'require_all', '~> 2.0.0'
27
+ s.add_dependency 'tty-command', '~> 0.10.1'
28
+ s.add_dependency 'tty-cursor', '~> 0.7.1'
29
+ s.add_dependency 'tty-pager', '~> 0.14.0'
30
+ s.add_dependency 'tty-progressbar', '~> 0.18.2'
31
+ s.add_dependency 'tty-spinner', '~> 0.9.3'
32
+ s.add_dependency 'vpsfree-client', '~> 0.19.0'
33
33
  end
data/docs/carrier.md ADDED
@@ -0,0 +1,138 @@
1
+ # confctl carriers
2
+ Carrier is a machine that can carry other machines. The concept was created
3
+ for the purpose of building netboot servers, but can be used in other settings
4
+ as well.
5
+
6
+ All machines must be defined within the cluster as usual. We can then designate
7
+ a machine that will serve as a carrier and provide a list of machines that it
8
+ will carry, e.g.:
9
+
10
+ ```nix
11
+ # File cluster/pxe-server/module.nix
12
+ cluster.pxe-server = {
13
+ # ...
14
+ carrier = {
15
+ enable = true;
16
+
17
+ # A list of machines found in the cluster/ directory that will be
18
+ # available on the netboot server. Note that you will have to create
19
+ # your own buildAttribute, so that the resulting path contains bzImage,
20
+ # initrd and possibly a machine.json file.
21
+ machines = [
22
+ {
23
+ machine = "node1";
24
+ buildAttribute = [ "system" "build" "dist" ];
25
+ }
26
+ ];
27
+ };
28
+ };
29
+ ```
30
+
31
+ Now the `node1` machine is available as two machines within the cluster:
32
+ `node1` and `pxe-server#node1`. We can build and deploy both:
33
+
34
+ * `confctl deploy node1` will deploy the target machine as defined by the configuration
35
+ * `confctl deploy pxe-server#node1` will build the machine and copy the result to `pxe-server`, its carrier
36
+
37
+ We need both commands to build a slightly different output, but based on the same
38
+ configuration. When deploying `node1`, confctl will build attribute
39
+ `config.system.build.toplevel`, where as deploying `pxe-server#node1` will build
40
+ attribute `config.system.build.dist`. This attribute is configured in `buildAttribute`
41
+ option in the example above. `config.system.build.dist` is defined within vpsAdminOS,
42
+ its output is a directory with kernel bzImage, initrd and the root filesystem
43
+ in a squashfs image, i.e. what we need for booting from network.
44
+
45
+ Custom build attributes can be created by the user. For example, this is how
46
+ `config.system.build.dist` would be defined for NixOS:
47
+
48
+ ```nix
49
+ # File cluster/nixos/config.nix
50
+ { config, pkgs, lib, confMachine, swpinsInfo, ... }:
51
+ let
52
+ # machine.json contains metadata about the machine that the carrier uses
53
+ # to assemble the netboot server
54
+ machineJson = pkgs.writeText "machine-${config.networking.hostName}.json" (builtins.toJSON {
55
+ # machine spin, nixos/vpsadminos
56
+ spin = "nixos";
57
+
58
+ # fully qualified domain name
59
+ fqdn = confMachine.host.fqdn;
60
+
61
+ # label used e.g. in user menus
62
+ label = confMachine.host.fqdn;
63
+
64
+ # path to the top-level derivation, needed for system boot
65
+ toplevel = builtins.unsafeDiscardStringContext config.system.build.toplevel;
66
+
67
+ # NixOS version and git revision
68
+ version = config.system.nixos.version;
69
+ revision = config.system.nixos.revision;
70
+
71
+ # MAC addresses for auto-detection
72
+ macs = confMachine.netboot.macs;
73
+
74
+ # Information used by confctl status
75
+ swpins-info = swpinsInfo;
76
+ });
77
+ in {
78
+ imports = [
79
+ <nixpkgs/nixos/modules/installer/netboot/netboot-minimal.nix>
80
+ ];
81
+
82
+ # Define custom build attribute
83
+ system.build.dist = pkgs.symlinkJoin {
84
+ name = "nixos-netboot";
85
+ paths = [
86
+ config.system.build.netbootRamdisk
87
+ config.system.build.kernel
88
+ config.system.build.netbootIpxeScript
89
+ ];
90
+
91
+ # Install machine.json
92
+ postBuild = ''
93
+ ln -s ${machineJson} $out/machine.json
94
+ '';
95
+ };
96
+
97
+ # other NixOS configuration
98
+ }
99
+ ```
100
+
101
+ The carrier must be configured to handle the carried machines. Netboot server
102
+ support is integrated within confctl and it only has to be enabled.
103
+
104
+ ```nix
105
+ # File cluster/pxe-server/config.nix
106
+ { config, ... }:
107
+ {
108
+ confctl.carrier.netboot = {
109
+ enable = true;
110
+
111
+ # IP address or hostname the netboot server will be available on
112
+ host = "192.168.100.5";
113
+
114
+ # IP ranges that will have access to the server
115
+ allowedIPv4Ranges = [
116
+ "192.168.100.0/24"
117
+ ];
118
+ };
119
+ }
120
+ ```
121
+
122
+ The netboot server is rebuilt whenever a machine is deployed to it.
123
+ The rebuild can be also run manually using command `build-netboot-server`.
124
+
125
+ ## Other uses
126
+ You can define your own commands to be run on the carrier machine when
127
+ images are deployed to it:
128
+
129
+ ```nix
130
+ # File cluster/pxe-server/config.nix
131
+ { config, ... }:
132
+ {
133
+ confctl.carrier.onChangeCommands = ''
134
+ # List profiles of carried machines
135
+ ls -l /nix/var/nix/profiles/confctl-*
136
+ '';
137
+ }
138
+ ```
@@ -546,6 +546,9 @@ module ConfCtl::Cli
546
546
  def nix_build_options(cmd)
547
547
  cmd.desc 'Maximum number of build jobs (see nix-build)'
548
548
  cmd.flag %w[j max-jobs], arg_name: 'number'
549
+
550
+ cmd.desc 'Number of CPU cores to be used (see nix-build)'
551
+ cmd.flag :cores, arg_name: 'number'
549
552
  end
550
553
  end
551
554
  end
@@ -110,7 +110,10 @@ module ConfCtl::Cli
110
110
  end
111
111
 
112
112
  def status
113
- machines = select_machines(args[0]).managed
113
+ machines = select_machines(args[0]).managed.select do |_host, machine|
114
+ machine.target_host || machine.carried?
115
+ end
116
+
114
117
  raise 'No machines to check' if machines.empty?
115
118
 
116
119
  ask_confirmation! do
@@ -244,7 +247,7 @@ module ConfCtl::Cli
244
247
  end
245
248
 
246
249
  def test_connection
247
- machines = select_machines_with_managed(args[0])
250
+ machines = select_machines_with_managed(args[0]).runnable
248
251
  raise 'No machines to test' if machines.empty?
249
252
 
250
253
  ask_confirmation! do
@@ -276,7 +279,7 @@ module ConfCtl::Cli
276
279
  end
277
280
 
278
281
  def ssh
279
- machines = select_machines_with_managed(args[0])
282
+ machines = select_machines_with_managed(args[0]).runnable
280
283
  raise 'No machines to ssh to' if machines.empty?
281
284
 
282
285
  if opts['input-string'] && opts['input-file']
@@ -295,7 +298,7 @@ module ConfCtl::Cli
295
298
  end
296
299
 
297
300
  def cssh
298
- machines = select_machines_with_managed(args[0])
301
+ machines = select_machines_with_managed(args[0]).runnable
299
302
  raise 'No machines to open cssh to' if machines.empty?
300
303
 
301
304
  ask_confirmation! do
@@ -508,38 +511,67 @@ module ConfCtl::Cli
508
511
  size: :auto,
509
512
  reserved_lines: 10
510
513
  ) do |lw|
511
- if opts['dry-activate-first']
512
- lw.sync_console do
513
- puts Rainbow(
514
- "Trying to activate configuration on #{host} " \
515
- "(#{machine.target_host})"
516
- ).yellow
517
- end
518
-
519
- raise "Error while activating configuration on #{host}" unless nix.activate(machine, toplevel, 'dry-activate')
514
+ if machine.carried?
515
+ deploy_carried_to_host(lw, nix, host, machine, toplevel, action)
516
+ else
517
+ deploy_standalone_to_host(lw, nix, host, machine, toplevel, action)
520
518
  end
521
519
 
520
+ lw.flush
521
+ end
522
+ end
523
+
524
+ def deploy_standalone_to_host(lw, nix, host, machine, toplevel, action)
525
+ if opts['dry-activate-first']
522
526
  lw.sync_console do
523
527
  puts Rainbow(
524
- "Activating configuration on #{host} (#{machine.target_host}): " \
525
- "#{action}"
528
+ "Trying to activate configuration on #{host} " \
529
+ "(#{machine.target_host})"
526
530
  ).yellow
527
531
  end
528
532
 
529
- return :skip if opts[:interactive] && !ask_confirmation(always: true)
533
+ raise "Error while activating configuration on #{host}" unless nix.activate(machine, toplevel, 'dry-activate')
534
+ end
530
535
 
531
- raise "Error while activating configuration on #{host}" unless nix.activate(machine, toplevel, action)
536
+ lw.sync_console do
537
+ puts Rainbow(
538
+ "Activating configuration on #{host} (#{machine.target_host}): " \
539
+ "#{action}"
540
+ ).yellow
541
+ end
532
542
 
533
- if %w[boot switch].include?(action) && !nix.set_profile(machine, toplevel)
534
- raise "Error while setting profile on #{host}"
535
- end
543
+ return :skip if opts[:interactive] && !ask_confirmation(always: true)
544
+
545
+ # rubocop:disable Style/GuardClause
546
+
547
+ unless nix.activate(machine, toplevel, action)
548
+ raise "Error while activating configuration on #{host}"
549
+ end
550
+
551
+ if %w[boot switch].include?(action) && !nix.set_profile(machine, toplevel)
552
+ raise "Error while setting profile on #{host}"
536
553
  end
554
+
555
+ # rubocop:enable Style/GuardClause
556
+ end
557
+
558
+ def deploy_carried_to_host(lw, nix, host, machine, toplevel, action)
559
+ return if action != 'switch'
560
+
561
+ # rubocop:disable Style/GuardClause
562
+ unless nix.set_carried_profile(machine, toplevel)
563
+ raise "Error while setting carried profile for #{host} on #{machine.carrier_machine}"
564
+ end
565
+ # rubocop:enable Style/GuardClause
537
566
  end
538
567
 
539
568
  def reboot_host(host, machine)
540
569
  if machine.localhost?
541
570
  puts Rainbow("Skipping reboot of #{host} as it is localhost").yellow
542
571
  return :skip
572
+ elsif machine.carried?
573
+ puts Rainbow("Skipping reboot of carried machine #{host}").yellow
574
+ return :skip
543
575
  end
544
576
 
545
577
  puts Rainbow("Rebooting #{host} (#{machine.target_host})").yellow
@@ -761,22 +793,16 @@ module ConfCtl::Cli
761
793
  machines.each do |host, machine|
762
794
  mc = ConfCtl::MachineControl.new(machine)
763
795
 
764
- begin
765
- puts "#{host}:" unless aggregate
796
+ puts "#{host}:" unless aggregate
766
797
 
767
- result = run_ssh_command_on_machine(mc, cmd)
798
+ result = run_ssh_command_on_machine(mc, cmd)
768
799
 
769
- if aggregate
770
- results[host] = result
771
- else
772
- puts result.out
773
- end
774
- rescue TTY::Command::ExitError => e
775
- if aggregate
776
- results[host] = e
777
- else
778
- puts e.message
779
- end
800
+ if aggregate
801
+ results[host] = result
802
+ elsif result.success?
803
+ puts result.out
804
+ else
805
+ puts result.err
780
806
  end
781
807
 
782
808
  puts unless aggregate
@@ -808,12 +834,8 @@ module ConfCtl::Cli
808
834
  tw.add do
809
835
  mc = ConfCtl::MachineControl.new(machine)
810
836
 
811
- begin
812
- result = run_ssh_command_on_machine(mc, cmd)
813
- results[host] = result
814
- rescue TTY::Command::ExitError => e
815
- results[host] = e
816
- end
837
+ result = run_ssh_command_on_machine(mc, cmd)
838
+ results[host] = result
817
839
 
818
840
  lw.sync_console { pb.advance }
819
841
  end
@@ -830,7 +852,13 @@ module ConfCtl::Cli
830
852
 
831
853
  results.each do |host, result|
832
854
  puts "#{host}:"
833
- puts result.out
855
+
856
+ if result.success?
857
+ puts result.out
858
+ else
859
+ puts result.err
860
+ end
861
+
834
862
  puts
835
863
  end
836
864
  end
@@ -844,7 +872,7 @@ module ConfCtl::Cli
844
872
  cmd_opts[:in] = opts['input-file']
845
873
  end
846
874
 
847
- mc.execute(*cmd, **cmd_opts)
875
+ mc.execute!(*cmd, **cmd_opts)
848
876
  end
849
877
 
850
878
  def process_aggregated_results(results)
@@ -903,7 +931,8 @@ module ConfCtl::Cli
903
931
  def do_build(machines)
904
932
  nix = ConfCtl::Nix.new(
905
933
  show_trace: opts['show-trace'],
906
- max_jobs: opts['max-jobs']
934
+ max_jobs: opts['max-jobs'],
935
+ cores: opts['cores']
907
936
  )
908
937
  hosts_swpin_paths = {}
909
938
 
@@ -996,7 +1025,7 @@ module ConfCtl::Cli
996
1025
  'Fetching [:bar] :current/:total (:percent)'
997
1026
  )
998
1027
 
999
- built_generations = nix.build_toplevels(
1028
+ built_generations = nix.build_attributes(
1000
1029
  hosts:,
1001
1030
  swpin_paths:,
1002
1031
  time:,
@@ -1,8 +1,11 @@
1
+ require_relative '../hook'
1
2
  require 'fileutils'
2
3
  require 'securerandom'
3
4
 
4
5
  module ConfCtl::Cli
5
6
  class Configuration < Command
7
+ ConfCtl::Hook.register :configuration_rediscover
8
+
6
9
  DIR_MODE = 0o755
7
10
  FILE_MODE = 0o644
8
11
 
@@ -238,6 +241,8 @@ module ConfCtl::Cli
238
241
 
239
242
  f.puts(']')
240
243
  end
244
+
245
+ ConfCtl::Hook.call(:configuration_rediscover)
241
246
  end
242
247
 
243
248
  protected
@@ -251,8 +256,8 @@ module ConfCtl::Cli
251
256
 
252
257
  entry_rel_path = File.join(*[rel_path, v].compact)
253
258
 
254
- if File.exist?(File.join(entry_abs_path, 'module.nix')) \
255
- && File.exist?(File.join(entry_abs_path, 'config.nix'))
259
+ if File.exist?(File.join(entry_abs_path, 'module.nix')) &&
260
+ File.exist?(File.join(entry_abs_path, 'config.nix'))
256
261
  ret << entry_rel_path
257
262
  end
258
263
 
@@ -78,10 +78,28 @@ module ConfCtl::Cli
78
78
  q.echo = false
79
79
  end.to_s
80
80
 
81
- @vpsadmin_client.authenticate(:basic, user:, password:)
81
+ @vpsadmin_client.authenticate(:token, user:, password:, lifetime: 'fixed', interval: 60) do |_action, params|
82
+ ret = {}
83
+
84
+ params.each do |name, desc|
85
+ ret[name] = read_auth_param(name, desc)
86
+ end
87
+
88
+ ret
89
+ end
90
+
82
91
  @vpsadmin_client
83
92
  end
84
93
 
94
+ def read_auth_param(name, p)
95
+ prompt = "#{p[:label] || name}: "
96
+
97
+ ask(prompt) do |q|
98
+ q.default = nil
99
+ q.echo = !p[:protected]
100
+ end
101
+ end
102
+
85
103
  def update_file(relpath, &)
86
104
  abs = File.join(data_dir, relpath)
87
105
  tmp = "#{abs}.new"
@@ -31,7 +31,7 @@ module ConfCtl::Cli
31
31
 
32
32
  return unless opts[:remote] && opts[:gc]
33
33
 
34
- machines_gc = machines.select do |host, _machine|
34
+ machines_gc = machines.runnable.select do |host, _machine|
35
35
  gens.detect { |gen| gen.host == host }
36
36
  end
37
37
 
@@ -68,7 +68,7 @@ module ConfCtl::Cli
68
68
 
69
69
  global = ConfCtl::Settings.instance.host_generations
70
70
 
71
- machines_gc = machines.select do |_host, machine|
71
+ machines_gc = machines.runnable.select do |_host, machine|
72
72
  gc = machine['buildGenerations']['collectGarbage']
73
73
 
74
74
  if gc.nil?
@@ -82,7 +82,7 @@ module ConfCtl::Cli
82
82
  end
83
83
 
84
84
  def collect_garbage
85
- machines = select_machines(args[0])
85
+ machines = select_machines(args[0]).runnable
86
86
 
87
87
  raise 'No machines to collect garbage on' if machines.empty?
88
88
 
@@ -295,6 +295,8 @@ module ConfCtl::Cli
295
295
  retvals = executor.run
296
296
  failed = retvals.compact
297
297
 
298
+ lw.flush
299
+
298
300
  raise "Gargabe collection failed on: #{failed.join(', ')}" if failed.any?
299
301
  end
300
302
  end
@@ -1,16 +1,16 @@
1
1
  module ConfCtl
2
2
  class Generation::HostList
3
3
  # @param mc [MachineControl]
4
+ # @param profile [String]
4
5
  # @return [Generation::HostList]
5
- def self.fetch(mc, profile: '/nix/var/nix/profiles/system')
6
- out, = mc.bash_script(<<-END
6
+ def self.fetch(mc, profile:)
7
+ out, = mc.bash_script(<<~END)
7
8
  realpath #{profile}
8
9
 
9
10
  for generation in `ls -d -1 #{profile}-*-link` ; do
10
11
  echo "$generation;$(readlink $generation);$(stat --format=%Y $generation)"
11
12
  done
12
13
  END
13
- )
14
14
 
15
15
  list = new(mc.machine.name)
16
16
  lines = out.strip.split("\n")
@@ -14,13 +14,13 @@ module ConfCtl
14
14
  @cmd = SystemCommand.new
15
15
  end
16
16
 
17
- def setup
17
+ def setup(ref: nil)
18
18
  File.stat(mirror_path)
19
19
  rescue Errno::ENOENT
20
20
  FileUtils.mkdir_p(mirror_path)
21
21
  git('clone', args: ['--mirror', url, mirror_path])
22
22
  else
23
- git_repo('fetch')
23
+ git_repo('fetch', opts: ['--no-show-forced-updates'], args: (ref ? ['origin', ref] : []))
24
24
  end
25
25
 
26
26
  def revision_parse(str)