confctl 2.2.2 → 3.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 +4 -4
- data/.git-hooks/pre_commit/nixfmt.rb +13 -0
- data/.github/workflows/rspec.yml +64 -0
- data/.github/workflows/rubocop.yml +27 -0
- data/.github/workflows/tests.yml +139 -0
- data/.gitignore +12 -7
- data/.overcommit.yml +2 -0
- data/.rspec +1 -0
- data/.rubocop.yml +8 -1
- data/AGENTS.md +42 -0
- data/CHANGELOG.md +57 -0
- data/Gemfile +3 -2
- data/Gemfile.lock +198 -0
- data/README.md +166 -58
- data/Rakefile +5 -0
- data/confctl.gemspec +20 -2
- data/docs/carrier.md +3 -3
- data/docs/flake-inputs.md +159 -0
- data/docs/swpins-to-flakes.md +325 -0
- data/example/cluster/module-list.nix +2 -1
- data/example/cluster/nixos-machine/config.nix +12 -2
- data/example/cluster/nixos-machine/hardware.nix +6 -1
- data/example/cluster/vpsadminos-container/config.nix +6 -1
- data/example/cluster/vpsadminos-container/module.nix +4 -1
- data/example/cluster/vpsadminos-machine/config.nix +6 -1
- data/example/cluster/vpsadminos-machine/hardware.nix +6 -1
- data/example/cluster/vpsadminos-machine/module.nix +5 -2
- data/example/cluster/vpsfreecz-vps/config.nix +6 -1
- data/example/cluster/vpsfreecz-vps/module.nix +4 -1
- data/example/configs/swpins.nix +8 -3
- data/example/environments/base.nix +6 -1
- data/example/swpins/core.json +35 -0
- data/example-flake/.gitignore +2 -0
- data/example-flake/README.md +38 -0
- data/example-flake/cluster/cluster.nix +5 -0
- data/example-flake/cluster/module-list.nix +4 -0
- data/example-flake/cluster/nested/nixos-machine/config.nix +25 -0
- data/example-flake/cluster/nested/nixos-machine/hardware.nix +9 -0
- data/example-flake/cluster/nested/nixos-machine/module.nix +8 -0
- data/example-flake/cluster/nixos-machine/config.nix +25 -0
- data/example-flake/cluster/nixos-machine/hardware.nix +9 -0
- data/example-flake/cluster/nixos-machine/module.nix +8 -0
- data/example-flake/cluster/vpsadminos-container/config.nix +28 -0
- data/example-flake/cluster/vpsadminos-container/module.nix +8 -0
- data/example-flake/cluster/vpsadminos-machine/config.nix +27 -0
- data/example-flake/cluster/vpsadminos-machine/hardware.nix +9 -0
- data/example-flake/cluster/vpsadminos-machine/module.nix +8 -0
- data/example-flake/cluster/vpsfreecz-vps/config.nix +31 -0
- data/example-flake/cluster/vpsfreecz-vps/module.nix +8 -0
- data/example-flake/configs/confctl.nix +10 -0
- data/example-flake/data/default.nix +5 -0
- data/example-flake/data/ssh-keys.nix +7 -0
- data/example-flake/environments/base.nix +18 -0
- data/example-flake/flake.lock +75 -0
- data/example-flake/flake.nix +36 -0
- data/example-flake/modules/module-list.nix +13 -0
- data/example-flake/shell.nix +11 -0
- data/flake.lock +159 -0
- data/flake.nix +189 -0
- data/gemset.nix +1022 -0
- data/lib/confctl/cli/app.rb +145 -0
- data/lib/confctl/cli/attr_filters.rb +1 -1
- data/lib/confctl/cli/cluster.rb +681 -108
- data/lib/confctl/cli/command.rb +24 -2
- data/lib/confctl/cli/configuration.rb +171 -106
- data/lib/confctl/cli/generation.rb +65 -1
- data/lib/confctl/cli/inputs/channels.rb +190 -0
- data/lib/confctl/cli/inputs/machines.rb +83 -0
- data/lib/confctl/cli/inputs/root.rb +101 -0
- data/lib/confctl/cli/inputs.rb +5 -0
- data/lib/confctl/cli/log_view.rb +21 -10
- data/lib/confctl/cli/migrate/swpins_to_flakes.rb +866 -0
- data/lib/confctl/cli/migrate.rb +5 -0
- data/lib/confctl/cli/output_formatter.rb +5 -7
- data/lib/confctl/cli/swpins/base.rb +9 -0
- data/lib/confctl/cli/swpins/channel.rb +2 -5
- data/lib/confctl/cli/swpins/cluster.rb +2 -5
- data/lib/confctl/cli/swpins/core.rb +2 -5
- data/lib/confctl/config_type.rb +7 -0
- data/lib/confctl/flake_lock.rb +78 -0
- data/lib/confctl/flake_lock_diff.rb +36 -0
- data/lib/confctl/generation/build.rb +131 -24
- data/lib/confctl/generation/build_list.rb +4 -3
- data/lib/confctl/generation/unified.rb +14 -1
- data/lib/confctl/git_repo_mirror.rb +2 -2
- data/lib/confctl/health_checks/run_command.rb +3 -2
- data/lib/confctl/health_checks/systemd/properties.rb +1 -1
- data/lib/confctl/health_checks/systemd/property_list.rb +2 -2
- data/lib/confctl/inputs/commit_message.rb +125 -0
- data/lib/confctl/inputs/git_commit.rb +17 -0
- data/lib/confctl/inputs/nix_output_guard.rb +37 -0
- data/lib/confctl/inputs/setter.rb +179 -0
- data/lib/confctl/inputs/updater.rb +76 -0
- data/lib/confctl/inputs.rb +5 -0
- data/lib/confctl/inputs_info.rb +50 -0
- data/lib/confctl/line_buffer.rb +1 -1
- data/lib/confctl/machine.rb +18 -3
- data/lib/confctl/machine_control.rb +8 -2
- data/lib/confctl/machine_list.rb +2 -2
- data/lib/confctl/machine_status.rb +63 -20
- data/lib/confctl/nix/args.rb +48 -0
- data/lib/confctl/nix.rb +30 -437
- data/lib/confctl/nix_build_flake.rb +95 -0
- data/lib/confctl/nix_copy.rb +14 -2
- data/lib/confctl/nix_flake.rb +449 -0
- data/lib/confctl/nix_format.rb +3 -3
- data/lib/confctl/nix_legacy.rb +467 -0
- data/lib/confctl/swpins/change_set.rb +29 -3
- data/lib/confctl/swpins/specs/base.rb +2 -2
- data/lib/confctl/ui.rb +19 -0
- data/lib/confctl/version.rb +1 -1
- data/man/index.html +11 -0
- data/man/man8/confctl-options.nix.8.html +113 -0
- data/man/man8/confctl.8 +152 -21
- data/man/man8/confctl.8.html +355 -0
- data/man/man8/confctl.8.md +148 -17
- data/man/style.css +301 -0
- data/nix/evaluator.nix +94 -69
- data/nix/flake/mk-confctl-devshell.nix +85 -0
- data/nix/flake/mk-confctl-outputs.nix +522 -0
- data/nix/flake/mk-config-devshell.nix +168 -0
- data/nix/lib/default.nix +118 -65
- data/nix/lib/machine/default.nix +65 -45
- data/nix/lib/machine/info.nix +16 -5
- data/nix/lib/swpins/eval.nix +42 -29
- data/nix/lib/swpins/options.nix +6 -2
- data/nix/machines.nix +23 -15
- data/nix/modules/cluster/default.nix +104 -41
- data/nix/modules/confctl/carrier/base.nix +11 -4
- data/nix/modules/confctl/carrier/carrier-env.rb +2 -2
- data/nix/modules/confctl/carrier/netboot/build-netboot-server.rb +50 -16
- data/nix/modules/confctl/carrier/netboot/nixos.nix +56 -24
- data/nix/modules/confctl/configuration-info.nix +17 -0
- data/nix/modules/confctl/generations.nix +2 -2
- data/nix/modules/confctl/host.nix +13 -0
- data/nix/modules/confctl/inputs-info.nix +21 -0
- data/nix/modules/confctl/kexec-netboot/default.nix +13 -6
- data/nix/modules/confctl/kexec-netboot/kexec-netboot.8.adoc +3 -0
- data/nix/modules/confctl/kexec-netboot/kexec-netboot.rb +25 -16
- data/nix/modules/confctl/nix.nix +31 -1
- data/nix/modules/confctl/swpins.nix +13 -6
- data/nix/modules/module-list.nix +4 -2
- data/nix/modules/system-list.nix +4 -1
- data/nix/package.nix +37 -0
- data/shell.nix +39 -8
- data/skills/confctl-configuration-update/SKILL.md +228 -0
- data/skills/confctl-configuration-update/agents/openai.yaml +4 -0
- data/skills/confctl-release/SKILL.md +102 -0
- data/skills/confctl-release/agents/openai.yaml +4 -0
- data/spec/confctl/cli/cluster_health_check_spec.rb +43 -0
- data/spec/confctl/cli/cluster_skip_current_deploy_spec.rb +157 -0
- data/spec/confctl/cli/cluster_status_flake_spec.rb +83 -0
- data/spec/confctl/cli/inputs_set_output_spec.rb +70 -0
- data/spec/confctl/configuration_spec.rb +45 -0
- data/spec/confctl/inputs_spec.rb +104 -0
- data/spec/confctl/machine_control_spec.rb +112 -0
- data/spec/confctl/machine_list_spec.rb +47 -0
- data/spec/confctl/nix_flake_spec.rb +41 -0
- data/spec/confctl/swpins_spec.rb +106 -0
- data/spec/generation/build_modes_spec.rb +132 -0
- data/spec/inputs/commit_message_spec.rb +205 -0
- data/spec/inputs/nix_cached_fallback_spec.rb +50 -0
- data/spec/inputs/nix_output_guard_spec.rb +45 -0
- data/spec/spec_helper.rb +12 -0
- data/spec/support/cli_helper.rb +57 -0
- data/test-runner.sh +7 -0
- data/tests/all-tests.nix +30 -0
- data/tests/make-test.nix +15 -0
- data/tests/runner/extensions/confctl_helpers.rb +238 -0
- data/tests/runner/extensions/hostfwd_ports.rb +41 -0
- data/tests/suite/auto_rollback.nix +344 -0
- data/tests/suite/carrier/deploy.nix +751 -0
- data/tests/suite/carrier/netboot.nix +849 -0
- data/tests/suite/deploy/base.nix +806 -0
- data/tests/suite/deploy/flakes.nix +1 -0
- data/tests/suite/deploy/swpins.nix +1 -0
- metadata +104 -4
- data/nix/modules/confctl/overlays.nix +0 -15
|
@@ -0,0 +1,228 @@
|
|
|
1
|
+
---
|
|
2
|
+
name: confctl-configuration-update
|
|
3
|
+
description: >-
|
|
4
|
+
Upgrade confctl-managed flake configuration repositories from one
|
|
5
|
+
NixOS/nixpkgs release to another, such as 25.11 to 26.05. Use only for NixOS
|
|
6
|
+
release upgrades that require reading target release notes, moving nixpkgs
|
|
7
|
+
release channels, updating release-coupled inputs such as home-manager when
|
|
8
|
+
needed, handling NixOS release deprecations, and validating machines with
|
|
9
|
+
confctl build. Do not use for routine flake input bumps, service updates,
|
|
10
|
+
dependency refreshes, or ordinary staging/production rollouts that do not
|
|
11
|
+
change the NixOS/nixpkgs release.
|
|
12
|
+
---
|
|
13
|
+
|
|
14
|
+
# confctl NixOS Release Upgrade
|
|
15
|
+
|
|
16
|
+
## Overview
|
|
17
|
+
|
|
18
|
+
Use this skill only for configuration repositories built by `confctl` when
|
|
19
|
+
upgrading machines from one NixOS/nixpkgs release to another. The goal is not
|
|
20
|
+
only to move release inputs, but to produce a reviewable, deployable release
|
|
21
|
+
port: release notes understood, generated input commits isolated, machines
|
|
22
|
+
built or explicitly blocked, warnings fixed, and compatibility implications
|
|
23
|
+
recorded.
|
|
24
|
+
|
|
25
|
+
Do not use this skill for routine flake input bumps, ordinary service updates,
|
|
26
|
+
dependency refreshes, or staging/production rollouts that keep the same NixOS
|
|
27
|
+
release.
|
|
28
|
+
|
|
29
|
+
## Rules
|
|
30
|
+
|
|
31
|
+
- Read local repository instructions before editing. Do not assume a specific
|
|
32
|
+
organization, directory layout, branch naming scheme, or tracking-file
|
|
33
|
+
convention.
|
|
34
|
+
- Use the repository's documented development environment. If it provides a
|
|
35
|
+
Nix shell, prefer `nix develop` before running `confctl`.
|
|
36
|
+
- Use `confctl` commands for flake input revision changes. Do not edit
|
|
37
|
+
`flake.lock` manually.
|
|
38
|
+
- Let `confctl` create generated input commits with `--commit`; use
|
|
39
|
+
`--no-editor` in non-interactive runs.
|
|
40
|
+
- Use `--no-changelog` for noisy inputs such as `nixpkgs`, `home-manager`, or
|
|
41
|
+
other large upstreams when they are moved as part of the release upgrade.
|
|
42
|
+
Keep changelogs for smaller controlled repositories when the log is useful.
|
|
43
|
+
- Treat Nix evaluation warnings and deprecation notices as work items. Fix
|
|
44
|
+
them before calling the port done unless the user explicitly accepts a
|
|
45
|
+
documented deferral.
|
|
46
|
+
- Try to build all existing machines. If local secrets, ISO images, private
|
|
47
|
+
paths, or other operator-only inputs block the sweep, record the exact
|
|
48
|
+
target and path, then continue with representative builds.
|
|
49
|
+
|
|
50
|
+
## Preparation
|
|
51
|
+
|
|
52
|
+
Before changing release inputs, identify and record the upgrade plan in
|
|
53
|
+
whatever place the project uses: an issue, PR description, local notes, or the
|
|
54
|
+
conversation. Include:
|
|
55
|
+
|
|
56
|
+
- source and target NixOS/nixpkgs releases;
|
|
57
|
+
- affected configuration repository and branch;
|
|
58
|
+
- intended channel order, for example shared stable hosts first, then staging,
|
|
59
|
+
then production;
|
|
60
|
+
- compatibility checks for persisted state, database schemas, service APIs,
|
|
61
|
+
generated configs, rollback, and mixed-version operation.
|
|
62
|
+
|
|
63
|
+
Read current release material from official NixOS sources. Do not rely on
|
|
64
|
+
memory for current release notes:
|
|
65
|
+
|
|
66
|
+
- NixOS release announcement;
|
|
67
|
+
- NixOS release notes for the target release;
|
|
68
|
+
- relevant nixpkgs, NixOS module, or service documentation linked from the
|
|
69
|
+
notes.
|
|
70
|
+
|
|
71
|
+
Extract a short checklist of likely issues: removed options, renamed packages
|
|
72
|
+
or aliases, default flips, required option changes, service module removals,
|
|
73
|
+
systemd behavior changes, filesystem changes, compiler/runtime changes, and
|
|
74
|
+
rollback-sensitive state changes.
|
|
75
|
+
|
|
76
|
+
## Inventory
|
|
77
|
+
|
|
78
|
+
From the configuration repository root:
|
|
79
|
+
|
|
80
|
+
```shell
|
|
81
|
+
nix develop
|
|
82
|
+
confctl inputs ls
|
|
83
|
+
confctl inputs channel ls
|
|
84
|
+
confctl ls
|
|
85
|
+
rg -n 'nixos-[0-9][0-9]\.[0-9][0-9]|release-[0-9][0-9]\.[0-9][0-9]|nixpkgs' \
|
|
86
|
+
flake.nix cluster configs modules overlays environments packages
|
|
87
|
+
```
|
|
88
|
+
|
|
89
|
+
If the repository does not use `nix develop`, use its documented shell or run
|
|
90
|
+
the same `confctl` commands with `confctl` available on `PATH`. Omit inventory
|
|
91
|
+
paths that do not exist in the target repository.
|
|
92
|
+
|
|
93
|
+
Map each release-related channel to the machines it affects. Common channel
|
|
94
|
+
roles include:
|
|
95
|
+
|
|
96
|
+
- `nixpkgs`: the nixpkgs input used by machines in a channel;
|
|
97
|
+
- `home-manager`: a release branch that often follows the same nixpkgs
|
|
98
|
+
release;
|
|
99
|
+
- `confctl`: the tool version used by the configuration shell, if the current
|
|
100
|
+
tool cannot evaluate or build the target release;
|
|
101
|
+
- service-specific roles: application, module, package, or operating-system
|
|
102
|
+
inputs consumed by a subset of machines and coupled to the NixOS release;
|
|
103
|
+
- environment channels: names such as `stable`, `staging`, `production`, or
|
|
104
|
+
project-specific equivalents.
|
|
105
|
+
|
|
106
|
+
Decide whether each channel should move now or remain a separate rollout. Do
|
|
107
|
+
not assume every environment channel must move together.
|
|
108
|
+
|
|
109
|
+
## Updating Inputs
|
|
110
|
+
|
|
111
|
+
Use `confctl` from the configuration repo's normal tool environment. Update
|
|
112
|
+
only inputs that are part of the release upgrade or are required to make the
|
|
113
|
+
target release evaluate. Common patterns:
|
|
114
|
+
|
|
115
|
+
```shell
|
|
116
|
+
nix develop -c confctl inputs channel update \
|
|
117
|
+
--commit --no-changelog --no-editor stable nixpkgs
|
|
118
|
+
|
|
119
|
+
nix develop -c confctl inputs channel update \
|
|
120
|
+
--commit --no-changelog --no-editor home-manager home-manager
|
|
121
|
+
|
|
122
|
+
nix develop -c confctl inputs channel set \
|
|
123
|
+
--commit --no-changelog --no-editor '{production,staging}' nixpkgs <rev>
|
|
124
|
+
|
|
125
|
+
nix develop -c confctl inputs channel set \
|
|
126
|
+
--commit --no-editor <channel> <role> <rev>
|
|
127
|
+
|
|
128
|
+
# Only when the current confctl input cannot handle the target release:
|
|
129
|
+
nix develop -c confctl inputs update \
|
|
130
|
+
--commit --no-changelog --no-editor confctl
|
|
131
|
+
|
|
132
|
+
# Only when pinning a specific confctl revision for the release upgrade:
|
|
133
|
+
nix develop -c confctl inputs set \
|
|
134
|
+
--commit --no-changelog --no-editor confctl <rev>
|
|
135
|
+
```
|
|
136
|
+
|
|
137
|
+
Use `channel update` when following the input's configured target release
|
|
138
|
+
branch/ref. Use `channel set` or `inputs set` for an exact revision, especially
|
|
139
|
+
when pinning an unmerged release-port branch or a known branch tip.
|
|
140
|
+
|
|
141
|
+
If a channel selector touches an input that is also used by another channel,
|
|
142
|
+
`confctl` can require `--allow-shared`. Only pass it after confirming the
|
|
143
|
+
shared move is intentional, and record the affected channels wherever the
|
|
144
|
+
project tracks validation.
|
|
145
|
+
|
|
146
|
+
Current `confctl` `set`/`update` commands own `flake.lock` revision changes
|
|
147
|
+
and commit only `flake.lock`. If a release port also needs `flake.nix` URL
|
|
148
|
+
refs changed, such as `github:NixOS/nixpkgs/nixos-25.11` to `nixos-26.05`,
|
|
149
|
+
treat that as a separate configuration edit. Never hand-edit `flake.lock`.
|
|
150
|
+
|
|
151
|
+
After each generated commit:
|
|
152
|
+
|
|
153
|
+
```shell
|
|
154
|
+
git show --stat --format=fuller HEAD
|
|
155
|
+
nix develop -c confctl inputs channel ls '<affected-channel-pattern>'
|
|
156
|
+
```
|
|
157
|
+
|
|
158
|
+
Keep generated `confctl` commit messages in their generated form unless
|
|
159
|
+
repository-local rules explicitly say to edit them.
|
|
160
|
+
|
|
161
|
+
## Build And Warning Loop
|
|
162
|
+
|
|
163
|
+
Start with a focused sample if the release upgrade is broad, then attempt the
|
|
164
|
+
full fleet:
|
|
165
|
+
|
|
166
|
+
```shell
|
|
167
|
+
nix develop -c confctl build -y '<critical-pattern>'
|
|
168
|
+
nix develop -c confctl build -y
|
|
169
|
+
```
|
|
170
|
+
|
|
171
|
+
Choose representative targets that cover every release-affected input and
|
|
172
|
+
machine type. Examples include one machine per environment channel, one
|
|
173
|
+
machine per custom nixpkgs input, critical infrastructure hosts, service hosts
|
|
174
|
+
that use release-coupled application inputs, and machines with unusual local
|
|
175
|
+
hardware or filesystem configuration.
|
|
176
|
+
|
|
177
|
+
Watch evaluation output and `confctl` logs for warnings:
|
|
178
|
+
|
|
179
|
+
```shell
|
|
180
|
+
rg -n -i 'warning:|deprecated|deprecat|renamed|obsolete|removed|will be removed' \
|
|
181
|
+
.confctl/logs
|
|
182
|
+
```
|
|
183
|
+
|
|
184
|
+
Fix warnings and evaluation failures in small logical commits. Common fixes
|
|
185
|
+
include:
|
|
186
|
+
|
|
187
|
+
- removed NixOS option: stop reading or setting it, or gate the common path;
|
|
188
|
+
- renamed package/alias: use the new package name;
|
|
189
|
+
- required filesystem option: set an explicit `fsType`;
|
|
190
|
+
- stale custom nixpkgs fork: replace the missing behavior locally, then remove
|
|
191
|
+
the unused input;
|
|
192
|
+
- package wrapper regression: add a local overlay/patch and verify the built
|
|
193
|
+
wrapper;
|
|
194
|
+
- local-only build input missing: record the path and target, then validate
|
|
195
|
+
neighboring machines that do not require it.
|
|
196
|
+
|
|
197
|
+
If the full build is blocked, keep reducing to direct targets until every
|
|
198
|
+
category touched by the release upgrade has either built or has a documented
|
|
199
|
+
external blocker. Record generation IDs, log paths, and blocked targets in the
|
|
200
|
+
project's normal validation notes.
|
|
201
|
+
|
|
202
|
+
## Compatibility Fix Commits
|
|
203
|
+
|
|
204
|
+
Keep release input bumps and functional fixes separate.
|
|
205
|
+
|
|
206
|
+
Recommended ordering:
|
|
207
|
+
|
|
208
|
+
1. Generated release-input commits.
|
|
209
|
+
2. Configuration fixes required by evaluation/build failures.
|
|
210
|
+
3. Warning/deprecation cleanup.
|
|
211
|
+
4. Removal of obsolete inputs after no machines use them.
|
|
212
|
+
5. Follow-up service-specific release fixes discovered by targeted builds.
|
|
213
|
+
|
|
214
|
+
For manual commits, use the repository's required commit workflow. Explain what
|
|
215
|
+
broke, why the new release requires the change, and what hosts/services are
|
|
216
|
+
affected. Do not put command transcripts or test logs in commit messages;
|
|
217
|
+
record validation in the project's normal notes or PR text.
|
|
218
|
+
|
|
219
|
+
## Completion
|
|
220
|
+
|
|
221
|
+
Before finishing:
|
|
222
|
+
|
|
223
|
+
- run repository formatting/hooks required by local rules;
|
|
224
|
+
- run `git status --short --branch`;
|
|
225
|
+
- record commands, validation results, warnings fixed, blockers, commit
|
|
226
|
+
hashes, and cleanup notes in the project-appropriate place;
|
|
227
|
+
- push the branch if requested or if that is the normal project flow;
|
|
228
|
+
- check CI when the repository has branch workflows.
|
|
@@ -0,0 +1,102 @@
|
|
|
1
|
+
---
|
|
2
|
+
name: confctl-release
|
|
3
|
+
description: Prepare and publish confctl version releases, including changelog, dependency metadata, final release commit and verified RubyGems artifact. Use for confctl releases, not releases of configurations managed by confctl.
|
|
4
|
+
---
|
|
5
|
+
|
|
6
|
+
# confctl releases
|
|
7
|
+
|
|
8
|
+
Read the repository's `AGENTS.md` and applicable workspace instructions first.
|
|
9
|
+
Distinguish release preparation from authorized publication. If the user asks
|
|
10
|
+
to prepare a release and wait, stop before default-branch integration, tagging
|
|
11
|
+
and RubyGems upload. Updating a release candidate does not expand that approval.
|
|
12
|
+
|
|
13
|
+
## Prepare the changes
|
|
14
|
+
|
|
15
|
+
1. Confirm the target version and inspect the latest release tag, current remote
|
|
16
|
+
default branch and RubyGems versions. Summarize material operator-visible
|
|
17
|
+
changes from the previous tag to the selected source, checking current code
|
|
18
|
+
and project guides. Record runtime requirements, upgrade steps and explicit
|
|
19
|
+
deprecation decisions. Preserve the dated `CHANGELOG.md` format.
|
|
20
|
+
2. Finish requested dependency updates and release-procedure changes before
|
|
21
|
+
the release commit. For a full lock refresh, run `bundle update` within the
|
|
22
|
+
existing Gemfile/gemspec constraints. Resolve using the minimum supported
|
|
23
|
+
Ruby from `confctl.gemspec`, not just the development shell's newer Ruby.
|
|
24
|
+
Inspect updates for runtime compatibility; changing declared constraints or
|
|
25
|
+
Ruby support is a separate compatibility decision.
|
|
26
|
+
3. Regenerate `gemset.nix` with Bundix after lock changes:
|
|
27
|
+
|
|
28
|
+
```sh
|
|
29
|
+
nix develop -c nix shell --inputs-from . nixpkgs#bundix -c bundix
|
|
30
|
+
nix develop -c nixfmt gemset.nix
|
|
31
|
+
```
|
|
32
|
+
|
|
33
|
+
`gemset.nix` is generated dependency metadata. Do not maintain it by hand.
|
|
34
|
+
`nix/package.nix` and the flake RSpec check pass `gemdir` to `bundlerEnv`,
|
|
35
|
+
which imports that directory's `gemset.nix`. The development shell instead
|
|
36
|
+
runs Bundler directly, so passing shell tests does not validate the Nix
|
|
37
|
+
package's gemset. Keep a dependency refresh in its own commit.
|
|
38
|
+
4. Update `lib/confctl/version.rb`, refresh the confctl path-gem version in
|
|
39
|
+
`Gemfile.lock` with Bundler, and regenerate `gemset.nix` with Bundix again.
|
|
40
|
+
Add the changelog entry. Verify all four release identifiers agree:
|
|
41
|
+
changelog, version constant, lockfile's `confctl` entry and gemset's
|
|
42
|
+
`confctl.version`. Other gems can legitimately have the old release's
|
|
43
|
+
version number; do not replace matching numbers globally.
|
|
44
|
+
5. Commit this release unit with subject `Version <version>` and a concise
|
|
45
|
+
rationale. The release commit must be the final commit, after dependency
|
|
46
|
+
and procedure changes. If preparation needs a correction, consolidate the
|
|
47
|
+
unmerged history so this remains true; preserve published default-branch
|
|
48
|
+
history. Run the declared hooks rather than bypassing them.
|
|
49
|
+
|
|
50
|
+
## Verify the candidate
|
|
51
|
+
|
|
52
|
+
Run quick metadata, syntax, whitespace and hook checks, then follow the
|
|
53
|
+
applicable independent final-review procedure for the complete committed
|
|
54
|
+
branch. Provide its full commit series, final diff and migration inventory.
|
|
55
|
+
After review, run the repository's RSpec, RuboCop and Nix formatting checks,
|
|
56
|
+
including the minimum supported Ruby when dependencies changed. Build the
|
|
57
|
+
flake's actual `confctl` package as well as the Ruby gem. Verify Nix's installed
|
|
58
|
+
confctl gem metadata matches the release version.
|
|
59
|
+
|
|
60
|
+
The gemspec uses a directory glob when `.git` is not a directory. In a Git
|
|
61
|
+
worktree this can include local `.gems`, `.bin`, caches or the `.git` file.
|
|
62
|
+
Build from a clean `git archive` export of the final commit, reusing the
|
|
63
|
+
provisioned Bundler dependency paths, then run `bundle exec rake build` there.
|
|
64
|
+
The rake build task generates the manual and HTML pages. Inspect the archive's
|
|
65
|
+
file list against committed source plus those generated files. Verify version,
|
|
66
|
+
Ruby requirement, executable and changelog, and retain the artifact's checksum.
|
|
67
|
+
Do not run `rake release` to prepare a candidate: it also tags, pushes and uploads.
|
|
68
|
+
|
|
69
|
+
For a packaged CLI smoke test, extract the gem and load its libraries directly.
|
|
70
|
+
Clear Bundler injection with `Bundler.with_unbundled_env` (or an equivalent
|
|
71
|
+
clean child environment), retain dependency `GEM_HOME`/`GEM_PATH`, and set
|
|
72
|
+
`RUBYLIB` to the extracted `lib`. Assert `ConfCtl.root` and the loaded
|
|
73
|
+
`confctl.rb` come from the extracted tree before running `--help`. In the
|
|
74
|
+
current CLI, GLI's `--version` is unset; inspect `ConfCtl::VERSION` and gemspec
|
|
75
|
+
metadata rather than accepting a version check that only exercises source code.
|
|
76
|
+
|
|
77
|
+
Publish the development branch according to repository/workspace policy and
|
|
78
|
+
check CI for its exact final head. When replacing a published candidate,
|
|
79
|
+
use the expected old head as the force-with-lease condition and handle
|
|
80
|
+
superseded CI under the applicable workspace rules. Present the final head,
|
|
81
|
+
diff, review/check results and artifact checksum for approval.
|
|
82
|
+
|
|
83
|
+
## Publish after authorization
|
|
84
|
+
|
|
85
|
+
Confirm authorization covers the repository's default branch, release tag and
|
|
86
|
+
RubyGems upload. Recheck the remote default branch and existing tag/version.
|
|
87
|
+
Integrate the approved branch under the repository/workspace Git policy. If
|
|
88
|
+
the source changes, rebuild and verify the artifact and reconcile approval
|
|
89
|
+
scope before publication. Keep the final release commit at the tip.
|
|
90
|
+
|
|
91
|
+
Create an annotated `v<version>` tag at the approved release commit and push
|
|
92
|
+
the authorized branch and tag over SSH. Upload the exact verified gem with
|
|
93
|
+
`gem push <artifact>` using the configured RubyGems authentication; never print
|
|
94
|
+
credentials. Verify that RubyGems reports the expected version, Ruby requirement
|
|
95
|
+
and artifact SHA-256, and that the remote tag resolves to the approved commit.
|
|
96
|
+
|
|
97
|
+
If any publication step fails, inspect which branch, tag and gem writes
|
|
98
|
+
succeeded before retrying. RubyGems versions cannot be overwritten. Do not
|
|
99
|
+
delete a tag, yank a gem, substitute a rebuilt artifact or bump the version
|
|
100
|
+
as an automatic recovery step. Record the partial result and obtain direction
|
|
101
|
+
for any action outside the approved release. Keep the session available for
|
|
102
|
+
follow-up under its lifecycle policy.
|
|
@@ -0,0 +1,43 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
require 'spec_helper'
|
|
4
|
+
require 'confctl'
|
|
5
|
+
require 'confctl/cli'
|
|
6
|
+
|
|
7
|
+
RSpec.describe ConfCtl::Cli::Cluster do
|
|
8
|
+
let(:gopts) { { color: 'never' } }
|
|
9
|
+
let(:opts) { { yes: true } }
|
|
10
|
+
let(:command) { described_class.new(gopts, opts, [nil]) }
|
|
11
|
+
|
|
12
|
+
def machine(managed: true, runnable: true, checks: [])
|
|
13
|
+
instance_double(
|
|
14
|
+
ConfCtl::Machine,
|
|
15
|
+
managed:,
|
|
16
|
+
runnable?: runnable,
|
|
17
|
+
health_checks: checks
|
|
18
|
+
)
|
|
19
|
+
end
|
|
20
|
+
|
|
21
|
+
it 'runs checks only for runnable machines' do
|
|
22
|
+
direct_check = instance_double(ConfCtl::HealthChecks::Base)
|
|
23
|
+
carried_check = instance_double(ConfCtl::HealthChecks::Base)
|
|
24
|
+
direct = machine(checks: [direct_check])
|
|
25
|
+
carried = machine(runnable: false, checks: [carried_check])
|
|
26
|
+
machines = ConfCtl::MachineList.new(
|
|
27
|
+
machines: {
|
|
28
|
+
'direct' => direct,
|
|
29
|
+
'carried' => carried
|
|
30
|
+
}
|
|
31
|
+
)
|
|
32
|
+
|
|
33
|
+
allow(command).to receive(:select_machines).with(nil).and_return(machines)
|
|
34
|
+
|
|
35
|
+
expect(command).to receive(:run_health_checks) do |selected, run_checks|
|
|
36
|
+
expect(selected.to_a).to eq([direct])
|
|
37
|
+
expect(run_checks).to eq([direct_check])
|
|
38
|
+
[]
|
|
39
|
+
end
|
|
40
|
+
|
|
41
|
+
command.health_check
|
|
42
|
+
end
|
|
43
|
+
end
|
|
@@ -0,0 +1,157 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
require 'spec_helper'
|
|
4
|
+
require 'confctl'
|
|
5
|
+
require 'confctl/cli'
|
|
6
|
+
|
|
7
|
+
RSpec.describe ConfCtl::Cli::Cluster do
|
|
8
|
+
let(:gopts) { { color: 'never' } }
|
|
9
|
+
let(:opts) { { yes: true } }
|
|
10
|
+
let(:command) { described_class.new(gopts, opts, []) }
|
|
11
|
+
let(:target_toplevel) { '/nix/store/target-system' }
|
|
12
|
+
let(:generation) do
|
|
13
|
+
instance_double(
|
|
14
|
+
ConfCtl::Generation::Build,
|
|
15
|
+
name: '2026-06-04--12-00-00',
|
|
16
|
+
toplevel: target_toplevel
|
|
17
|
+
)
|
|
18
|
+
end
|
|
19
|
+
|
|
20
|
+
def status_with(current_toplevel:, profile_toplevel: nil)
|
|
21
|
+
profile_generation =
|
|
22
|
+
if profile_toplevel
|
|
23
|
+
instance_double(ConfCtl::Generation::Host, toplevel: profile_toplevel)
|
|
24
|
+
end
|
|
25
|
+
|
|
26
|
+
generations =
|
|
27
|
+
if profile_generation
|
|
28
|
+
instance_double(ConfCtl::Generation::HostList, current: profile_generation)
|
|
29
|
+
end
|
|
30
|
+
|
|
31
|
+
instance_double(
|
|
32
|
+
ConfCtl::MachineStatus,
|
|
33
|
+
current_toplevel:,
|
|
34
|
+
generations:
|
|
35
|
+
)
|
|
36
|
+
end
|
|
37
|
+
|
|
38
|
+
def machine_with(carried:)
|
|
39
|
+
instance_double(ConfCtl::Machine, carried?: carried)
|
|
40
|
+
end
|
|
41
|
+
|
|
42
|
+
def already_using_target?(action:, current_toplevel:, profile_toplevel:, carried: false)
|
|
43
|
+
command.send(
|
|
44
|
+
:already_using_target_generation?,
|
|
45
|
+
machine_with(carried:),
|
|
46
|
+
status_with(current_toplevel:, profile_toplevel:),
|
|
47
|
+
generation,
|
|
48
|
+
action
|
|
49
|
+
)
|
|
50
|
+
end
|
|
51
|
+
|
|
52
|
+
it 'skips standalone boot and switch when runtime and profile match' do
|
|
53
|
+
%w[boot switch].each do |action|
|
|
54
|
+
expect(
|
|
55
|
+
already_using_target?(
|
|
56
|
+
action:,
|
|
57
|
+
current_toplevel: target_toplevel,
|
|
58
|
+
profile_toplevel: target_toplevel
|
|
59
|
+
)
|
|
60
|
+
).to be(true)
|
|
61
|
+
end
|
|
62
|
+
end
|
|
63
|
+
|
|
64
|
+
it 'does not skip standalone boot and switch when the profile is stale' do
|
|
65
|
+
%w[boot switch].each do |action|
|
|
66
|
+
expect(
|
|
67
|
+
already_using_target?(
|
|
68
|
+
action:,
|
|
69
|
+
current_toplevel: target_toplevel,
|
|
70
|
+
profile_toplevel: '/nix/store/old-system'
|
|
71
|
+
)
|
|
72
|
+
).to be(false)
|
|
73
|
+
end
|
|
74
|
+
end
|
|
75
|
+
|
|
76
|
+
it 'skips standalone test and dry-activate when runtime matches' do
|
|
77
|
+
%w[test dry-activate].each do |action|
|
|
78
|
+
expect(
|
|
79
|
+
already_using_target?(
|
|
80
|
+
action:,
|
|
81
|
+
current_toplevel: target_toplevel,
|
|
82
|
+
profile_toplevel: '/nix/store/old-system'
|
|
83
|
+
)
|
|
84
|
+
).to be(true)
|
|
85
|
+
end
|
|
86
|
+
end
|
|
87
|
+
|
|
88
|
+
it 'skips carried machines when their carrier-managed profile matches' do
|
|
89
|
+
expect(
|
|
90
|
+
already_using_target?(
|
|
91
|
+
action: 'switch',
|
|
92
|
+
current_toplevel: target_toplevel,
|
|
93
|
+
profile_toplevel: nil,
|
|
94
|
+
carried: true
|
|
95
|
+
)
|
|
96
|
+
).to be(true)
|
|
97
|
+
end
|
|
98
|
+
|
|
99
|
+
it 'does not skip carried machines when their carrier-managed profile is stale' do
|
|
100
|
+
expect(
|
|
101
|
+
already_using_target?(
|
|
102
|
+
action: 'switch',
|
|
103
|
+
current_toplevel: '/nix/store/old-system',
|
|
104
|
+
profile_toplevel: nil,
|
|
105
|
+
carried: true
|
|
106
|
+
)
|
|
107
|
+
).to be(false)
|
|
108
|
+
end
|
|
109
|
+
|
|
110
|
+
it 'does not skip when status data is unavailable' do
|
|
111
|
+
expect(
|
|
112
|
+
command.send(
|
|
113
|
+
:already_using_target_generation?,
|
|
114
|
+
machine_with(carried: false),
|
|
115
|
+
status_with(current_toplevel: nil, profile_toplevel: target_toplevel),
|
|
116
|
+
generation,
|
|
117
|
+
'switch'
|
|
118
|
+
)
|
|
119
|
+
).to be(false)
|
|
120
|
+
end
|
|
121
|
+
|
|
122
|
+
it 'does not pre-filter copy-only deployments' do
|
|
123
|
+
copy_command = described_class.new(gopts, opts.merge('copy-only' => true), [])
|
|
124
|
+
machine = machine_with(carried: false)
|
|
125
|
+
machines = ConfCtl::MachineList.new(machines: { 'host' => machine })
|
|
126
|
+
host_generations = { 'host' => generation }
|
|
127
|
+
|
|
128
|
+
allow(copy_command).to receive(:deploy_target_statuses).and_raise('unexpected status query')
|
|
129
|
+
|
|
130
|
+
filtered_machines, filtered_generations =
|
|
131
|
+
copy_command.send(:skip_current_deploy_targets, machines, host_generations, 'switch')
|
|
132
|
+
|
|
133
|
+
expect(filtered_machines).to equal(machines)
|
|
134
|
+
expect(filtered_generations).to equal(host_generations)
|
|
135
|
+
end
|
|
136
|
+
|
|
137
|
+
it 'removes skipped hosts from deploy inputs' do
|
|
138
|
+
machine = machine_with(carried: false)
|
|
139
|
+
machines = ConfCtl::MachineList.new(machines: { 'host' => machine })
|
|
140
|
+
host_generations = { 'host' => generation }
|
|
141
|
+
|
|
142
|
+
allow(command).to receive(:deploy_target_statuses).and_return(
|
|
143
|
+
'host' => status_with(
|
|
144
|
+
current_toplevel: target_toplevel,
|
|
145
|
+
profile_toplevel: target_toplevel
|
|
146
|
+
)
|
|
147
|
+
)
|
|
148
|
+
|
|
149
|
+
expect do
|
|
150
|
+
@filtered_machines, @filtered_generations =
|
|
151
|
+
command.send(:skip_current_deploy_targets, machines, host_generations, 'switch')
|
|
152
|
+
end.to output(/Skipping host: already using target generation 2026-06-04--12-00-00/).to_stdout
|
|
153
|
+
|
|
154
|
+
expect(@filtered_machines).to be_empty
|
|
155
|
+
expect(@filtered_generations).to be_empty
|
|
156
|
+
end
|
|
157
|
+
end
|
|
@@ -0,0 +1,83 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
require 'spec_helper'
|
|
4
|
+
require 'confctl'
|
|
5
|
+
require 'confctl/cli'
|
|
6
|
+
|
|
7
|
+
RSpec.describe ConfCtl::Cli::Cluster do
|
|
8
|
+
let(:gopts) { { color: 'never' } }
|
|
9
|
+
let(:opts) { { yes: true, generation: 'none' } }
|
|
10
|
+
let(:command) { described_class.new(gopts, opts, [nil]) }
|
|
11
|
+
let(:host) { 'cz.vpsfree/vpsadmin/int.api1' }
|
|
12
|
+
let(:machine) do
|
|
13
|
+
instance_double(
|
|
14
|
+
'ConfCtl::Machine',
|
|
15
|
+
managed: true,
|
|
16
|
+
target_host: 'int.api1',
|
|
17
|
+
carried?: false
|
|
18
|
+
)
|
|
19
|
+
end
|
|
20
|
+
let(:machines) { ConfCtl::MachineList.new(machines: { host => machine }) }
|
|
21
|
+
let(:status_class) do
|
|
22
|
+
Struct.new(
|
|
23
|
+
:uptime,
|
|
24
|
+
:inputs_info,
|
|
25
|
+
:target_inputs_info,
|
|
26
|
+
:target_toplevel,
|
|
27
|
+
:current_toplevel,
|
|
28
|
+
:generations
|
|
29
|
+
) do
|
|
30
|
+
def query(**)
|
|
31
|
+
nil
|
|
32
|
+
end
|
|
33
|
+
end
|
|
34
|
+
end
|
|
35
|
+
let(:status) do
|
|
36
|
+
status_class.new(
|
|
37
|
+
31.7 * 24 * 60 * 60,
|
|
38
|
+
{
|
|
39
|
+
'nixpkgs' => { 'rev' => '71caefce01234567', 'shortRev' => '71caefce' },
|
|
40
|
+
'vpsadmin' => { 'rev' => 'cb29516601234567', 'shortRev' => 'cb295166' }
|
|
41
|
+
},
|
|
42
|
+
nil,
|
|
43
|
+
nil,
|
|
44
|
+
nil,
|
|
45
|
+
nil
|
|
46
|
+
)
|
|
47
|
+
end
|
|
48
|
+
let(:nix) { instance_double(ConfCtl::Nix) }
|
|
49
|
+
let(:build_generations) { instance_double(ConfCtl::Generation::BuildList, count: 29) }
|
|
50
|
+
|
|
51
|
+
before do
|
|
52
|
+
allow(command).to receive(:select_machines).with(nil).and_return(machines)
|
|
53
|
+
allow(ConfCtl::MachineStatus).to receive(:new).with(machine).and_return(status)
|
|
54
|
+
allow(ConfCtl::Nix).to receive(:new).and_return(nix)
|
|
55
|
+
allow(nix).to receive(:eval_inputs_info).with(host).and_return(
|
|
56
|
+
'nixpkgs' => { 'rev' => 'fea3b36789abcdef', 'shortRev' => 'fea3b367' },
|
|
57
|
+
'vpsadmin' => { 'rev' => 'cb29516601234567', 'shortRev' => 'cb295166' }
|
|
58
|
+
)
|
|
59
|
+
allow(ConfCtl::Generation::BuildList).to receive(:new).with(host).and_return(build_generations)
|
|
60
|
+
end
|
|
61
|
+
|
|
62
|
+
it 'shows deployed input revisions without target arrows' do
|
|
63
|
+
captured_rows = nil
|
|
64
|
+
captured_cols = nil
|
|
65
|
+
|
|
66
|
+
allow(ConfCtl::Cli::OutputFormatter).to receive(:print) do |rows, cols, **|
|
|
67
|
+
captured_rows = rows
|
|
68
|
+
captured_cols = cols
|
|
69
|
+
end
|
|
70
|
+
|
|
71
|
+
command.status_flake
|
|
72
|
+
|
|
73
|
+
expect(captured_cols).to include('nixpkgs', 'vpsadmin')
|
|
74
|
+
|
|
75
|
+
row = captured_rows.fetch(0)
|
|
76
|
+
|
|
77
|
+
expect(row['status'].to_s).to eq('outdated')
|
|
78
|
+
expect(row['nixpkgs'].to_s).to eq('71caefce')
|
|
79
|
+
expect(row['vpsadmin'].to_s).to eq('cb295166')
|
|
80
|
+
expect(row['nixpkgs'].to_s).not_to include('->')
|
|
81
|
+
expect(row['vpsadmin'].to_s).not_to include('->')
|
|
82
|
+
end
|
|
83
|
+
end
|