empeira 0.1.0.pre.alpha.1 → 0.1.0.pre.alpha.3

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: de7c894839d24da7b03726dc8daae007f1c3e97ecbca32330ae451b1265409c0
4
- data.tar.gz: '008e104e79374009b3811f365df8e301cc9a78157af5f5b6d841d32c2367b54d'
3
+ metadata.gz: c856b6d473f798e1882b261f6dec67a20ebaa89142a073ba9b977265cd24cd6f
4
+ data.tar.gz: ac23a47ef8ecf4abfdd8d48557cee762f5954e69c40c335b12e7d6428e376709
5
5
  SHA512:
6
- metadata.gz: 8d6a4db00084f8e013f208e655313edbaf032b320507840220a416e8eb98644ad2d6d9a74d5db21dbc9298e2d7aa5243d78d805e0da1c0dfc043501e2dfb8630
7
- data.tar.gz: d452d8412d487fc9800fcc549b8be52f34068093f71fdca0b70778dadf941ce0c674cd87974dbe2b00bd0aea869d04e855aef8d03eecb3a0e79d624394f311a7
6
+ metadata.gz: 30b50d1c33a73512fdd2067064e0f17b63dda84204045b90994137ec79ee8374c466501b232119ea2fff3b81f9dda39c62edd8fed47b5ee50a528f75f063dbf6
7
+ data.tar.gz: 8daa8af9f4f9bb5ffcc1751cf3c8714e6853142dd59878ba6791150b79511549b626cc3edd9eafc95e36adfed3b00f793e8f97d78a607ac6d29dd70865e91dc4
data/README.md CHANGED
@@ -5,7 +5,7 @@
5
5
  [![Build & Tests](https://img.shields.io/github/actions/workflow/status/c8m6/empeira/ci.yml?branch=main&label=Build%20%26%20Tests)](https://github.com/c8m6/empeira/actions/workflows/ci.yml)
6
6
  [![OpenVox E2E](https://img.shields.io/github/actions/workflow/status/c8m6/empeira/openvox.yml?branch=main&label=OpenVox%20E2E)](https://github.com/c8m6/empeira/actions/workflows/openvox.yml)
7
7
  [![Release](https://img.shields.io/github/v/release/c8m6/empeira?include_prereleases&sort=date&label=Release)](https://github.com/c8m6/empeira/releases)
8
- [![Ruby: 3.4 / 4.0](https://img.shields.io/badge/Ruby-3.4%20%7C%204.0-CC342D)](docs/installation.md)
8
+ [![Ruby: 3.1–4.0](https://img.shields.io/badge/Ruby-3.1%20%7C%203.2%20%7C%203.3%20%7C%203.4%20%7C%204.0-CC342D)](docs/installation.md)
9
9
  [![License: AGPL-3.0-only](https://img.shields.io/badge/License-AGPL--3.0--only-blue)](LICENSE)
10
10
 
11
11
  **ἐμπειρία (empeiría)** — experience gained through practice. The name reflects
@@ -46,35 +46,37 @@ signed repositories or verified package artifacts are selected separately.
46
46
  - An internal Chromium desktop and small additional infrastructure services.
47
47
  - Explicit Puppetfile module and image updates.
48
48
 
49
- Hosts: Linux, macOS and Windows through WSL2/Linux. Ruby 3.4 is the minimum;
50
- Ruby 3.4 and 4.0 are supported; CI runs deterministic tests with both versions.
49
+ Hosts: Linux, macOS and Windows through WSL2/Linux. Ruby 3.1 is the minimum; Ruby 3.1, 3.2, 3.3, 3.4 and 4.0 are supported;
50
+ CI runs deterministic tests and official addon/Core acceptance on all five versions
51
+ on Linux and macOS, plus the existing Linux Docker/OpenVox smoke on every Ruby.
51
52
  Podman with Netavark is the default; Docker requires a Linux engine 28+ with
52
53
  isolated bridge support. VMs require usable KVM or HVF. Platform setup and
53
54
  validation limits are in [installation](docs/installation.md).
54
55
 
55
56
  ## Installation
56
57
 
57
- Until a release is available, install from source with Ruby, Bundler and Git:
58
+ The [Empeira gem on RubyGems.org](https://rubygems.org/gems/empeira) is the
59
+ recommended installation. With Ruby 3.1, 3.2, 3.3, 3.4 or 4.0 already installed:
58
60
 
59
61
  ```bash
60
- git clone https://github.com/c8m6/empeira.git
61
- cd empeira
62
- bundle install
63
- export EMPEIRA_SOURCE="$PWD"
64
- empeira() { BUNDLE_GEMFILE="$EMPEIRA_SOURCE/Gemfile" bundle exec ruby "$EMPEIRA_SOURCE/bin/empeira" "$@"; }
62
+ gem install --user-install empeira --pre
63
+ export PATH="$(ruby -r rubygems -e 'puts Gem.user_dir')/bin:$PATH"
65
64
  empeira --version
66
65
  ```
67
66
 
68
- The function lets you invoke the source CLI from your control repository. See
69
- [installation](docs/installation.md) for runtime prerequisites, release-gem
70
- installation and Bash completion.
67
+ `--pre` enables prerelease installation, including the first alpha
68
+ [`v0.1.0-alpha.1`](https://github.com/c8m6/empeira/releases/tag/v0.1.0-alpha.1).
69
+ No source checkout or Bundler is needed for a gem installation. Install Git,
70
+ a supported container runtime and any optional VM tools separately; see
71
+ [installation](docs/installation.md) for host prerequisites, exact-version
72
+ installation and Bash completion. Developers can also
73
+ [run from source](docs/installation.md#source-installation-and-project-opt-in).
71
74
 
72
75
  ## Quickstart
73
76
 
74
77
  Use a dedicated Git control repository and an empty configuration marker:
75
78
 
76
79
  ```bash
77
- cd ..
78
80
  mkdir control-repo
79
81
  cd control-repo
80
82
  git init
@@ -126,8 +128,10 @@ See [diagnostic access and recovery](docs/nodes.md#root-diagnostic-commands-and-
126
128
 
127
129
  ## Alpha status
128
130
 
129
- empeira is being prepared for its first public alpha. Interfaces and inventory
130
- schemas may change; unsupported inventory fails closed without automatic migration.
131
+ The first public alpha, [`v0.1.0-alpha.1`](https://github.com/c8m6/empeira/releases/tag/v0.1.0-alpha.1),
132
+ is available on [RubyGems.org](https://rubygems.org/gems/empeira). Interfaces
133
+ and inventory schemas may change; unsupported inventory fails closed without
134
+ automatic migration.
131
135
  Use synthetic control code and disposable credentials. The shared network is a
132
136
  cooperative lab, and its internal PuppetDB API is unauthenticated.
133
137
 
@@ -135,7 +139,7 @@ Container process mode does not provide a full booted OS. Systemd requires Podma
135
139
  and cgroup v2. VM and shared-network support require actual host capabilities;
136
140
  macOS/HVF and arm64 runtime validation are manual gates. Native Windows, automatic
137
141
  self-update, aggregate `update all` and standalone platform binaries are unavailable.
138
- The initial release distribution is a Ruby gem.
142
+ Distribution is through RubyGems.org and the matching GitHub Release gem asset.
139
143
 
140
144
  ## Community and license
141
145
 
data/docs/addons.md CHANGED
@@ -329,3 +329,15 @@ is incomplete, stop users of that source and remove only its affected source
329
329
  namespace before synchronizing again; never delete a live lock as recovery.
330
330
  For missing command completion, inspect `addons info NAME` and source the current
331
331
  Bash completion again after an Empeira upgrade.
332
+
333
+ ## Ruby support and addon development
334
+
335
+ Core and the official Convert, Pre-Commit and Sync addons support Ruby 3.1, 3.2,
336
+ 3.3, 3.4 and 4.0 on Linux, macOS and WSL2. Ruby 3.1 is the minimum. Keep API 1
337
+ entrypoints runnable directly, preserve argv, terminal streams, environment and
338
+ exit status, and explicitly require libraries that newer Rubies no longer ship.
339
+ Do not install gems implicitly. Convert's XML support needs user-installed REXML.
340
+ Test complete standalone behavior and actual execution through Core on all five
341
+ Rubies; syntax checks alone are insufficient. See the [development gate](development.md#ruby-compatibility)
342
+ for the required Linux/macOS matrix, pinned source checkouts and local commands.
343
+ Real container/VM and WSL2 capabilities require their own integration gates.
data/docs/development.md CHANGED
@@ -2,10 +2,10 @@
2
2
 
3
3
  ## Environment and quality checks
4
4
 
5
- Install Git, Ruby 3.4, Bundler and the platform prerequisites in
6
- [installation](installation.md). The gem requires Ruby 3.4+; Ruby 3.2 and 3.3 are
7
- unsupported. Normal Ubuntu CI runs deterministic RSpec on Ruby 3.4 and 4.0, with
8
- RuboCop and both dependency audits only on Ruby 3.4. Release builds use Ruby 3.4.
5
+ Install Git, Ruby 3.1+, Bundler and the platform prerequisites in
6
+ [installation](installation.md). The gem requires Ruby 3.1+; normal Ubuntu and macOS CI
7
+ runs deterministic RSpec on Ruby 3.1, 3.2, 3.3, 3.4 and 4.0, with
8
+ RuboCop and both dependency audits only on Ubuntu with Ruby 3.4. Release builds use Ruby 3.4.
9
9
  Use synthetic control repositories and disposable credentials.
10
10
 
11
11
  ```console
@@ -20,7 +20,7 @@ bundle exec bundler-audit check --gemfile-lock resources/modules/Gemfile.lock
20
20
  local child processes and loopback fixtures. Explicit integration gates are pending
21
21
  unless enabled; a pending test is not a successful runtime check. Security auditing
22
22
  covers known Ruby advisories in both resolutions, not all container OS packages.
23
- To reproduce either CI test job, activate the corresponding Ruby, install the locked
23
+ To reproduce a CI test job, activate the corresponding Ruby, install the locked
24
24
  bundle and run `bundle exec rspec --exclude-pattern 'spec/integration/**/*_spec.rb'`.
25
25
  Runtime and VM compatibility remain separate manual gates.
26
26
 
@@ -93,18 +93,73 @@ For workflow changes, parse `.github/workflows/*.yml` and run `actionlint` when
93
93
  available. Review action pins, triggers and permissions as well as YAML syntax.
94
94
  See [contributing](../CONTRIBUTING.md) for review, commit and label conventions.
95
95
 
96
- ### Ruby 4.0 compatibility
96
+ ### Ruby compatibility
97
97
 
98
- Ruby 3.4 and 4.0 are supported. The deterministic suite passed locally on Linux
99
- x86_64 with Ruby 3.4.7 and 4.0.7. Ruby 4.0 validation used an official Ruby container;
100
- it does not establish macOS, container-engine or accelerated VM integration results.
98
+ Ruby 3.1 is the minimum. Ruby 3.1, 3.2, 3.3, 3.4 and 4.0 run the complete
99
+ deterministic suite on Ubuntu 24.04 and native macOS 15 in regular CI. Every
100
+ combination is required, including Ruby 4.0. The suite covers the CLI, Bash
101
+ completion, providers, networking, bootstrap, updates, addon contracts and gem
102
+ build/install/start. The existing Docker/OpenVox node smoke also runs on all five
103
+ Rubies on Ubuntu, with at most two concurrent jobs. Broader manual runtime
104
+ integrations and release builds remain on Ruby 3.4;
105
+ a unit test does not establish Docker, rootless Podman, KVM/HVF or WSL2 results.
101
106
 
102
- Source development uses Bundler 4.0.22 from `Gemfile.lock`, matching the existing
103
- module-helper tooling. With Ruby 4.0.7, the previous Bundler 2.4.19 redefined
104
- `Gem::Platform` constants and emitted warnings in child processes. This failed 14
105
- strict stderr, streaming, Bash completion and Puppetfile installer tests. Updating
106
- Bundler preserves those output assertions; no application output filtering or
107
- warning suppression is used. The dependency gem versions are unchanged.
107
+ Both committed lockfiles use Bundler 2.6.9, compatible with Ruby 3.1. Ruby 4.0
108
+ requires the explicit Bundler 4.0.22 selection to avoid RubyGems platform-constant
109
+ warnings. Frozen installation keeps the same dependencies and lockfiles on every
110
+ Ruby. To reproduce Ruby 4 CI without editing either lockfile:
111
+
112
+ ```sh
113
+ gem install bundler -v 4.0.22 --no-document
114
+ export BUNDLER_VERSION=4.0.22 BUNDLE_FROZEN=true
115
+ bundle install
116
+ bundle exec rspec --exclude-pattern 'spec/integration/**/*_spec.rb'
117
+ ```
118
+
119
+ For Ruby 3.x, use `BUNDLER_VERSION=2.6.9` instead. The module-helper image uses
120
+ Bundler 2.6.9 on its separately pinned Ruby base. r10k's transitive dependencies
121
+ `fast_gettext` and `multi_json` remain on 3.1.x and 1.18.0 respectively because
122
+ newer versions require Ruby 3.2. Other locked gem versions are retained. Ruby 3.1's
123
+ internal Data compatibility implementation preserves positional and keyword
124
+ constructors (including `[]` and custom initializers), frozen copies and empty
125
+ record value semantics. Newer Rubies retain their native Data implementation. Both
126
+ locks contain registry SHA-256 checksums generated by Bundler, not hand-entered:
127
+
128
+ ```sh
129
+ bundle _2.6.9_ lock --add-checksums
130
+ BUNDLE_GEMFILE=resources/modules/Gemfile bundle _2.6.9_ lock --add-checksums
131
+ git diff -- Gemfile.lock resources/modules/Gemfile.lock
132
+ ```
133
+
134
+ The real official addon acceptance gate is also required in every Linux/macOS
135
+ matrix job. CI checks out reviewed addon commits, then installs them through
136
+ Core's actual local Git acquisition, cache, manifest, configuration and execution
137
+ paths. Synthetic control/Hiera repositories, a real PTY and a local rsync stub
138
+ cover revision updates, failed-update retention, argv, inherited environment,
139
+ configuration exports, native errors and signal-derived status. No real data or
140
+ external synchronization endpoints are used. With the three addon checkouts
141
+ under a common directory, run:
142
+
143
+ ```sh
144
+ EMPEIRA_OFFICIAL_ADDON_ROOT=/path/to/checkouts \
145
+ bundle exec rspec spec/acceptance/official_addons.rb
146
+ ```
147
+
148
+ Each checkout must be named `empeira-addon-convert`, `empeira-addon-pre-commit`
149
+ or `empeira-addon-sync`. Set `EMPEIRA_OFFICIAL_ADDONS=sync` (or a comma-separated
150
+ subset) to select checkouts; absent sources fail the gate. Addon CI uses the
151
+ same gate against a pinned Core revision. Update the reviewed commit pins in
152
+ CI when changing either side of API 1. Standalone addon unit tests remain in
153
+ each addon's matrix. macOS installs GNU coreutils and GNU tar for fixtures that
154
+ execute Linux guest package-manager commands against synthetic roots; these are
155
+ test dependencies, not macOS product prerequisites. Fixtures canonicalize temporary
156
+ paths, keep Unix socket names short and declare synthetic CPU architectures.
157
+ The single Python Linux-guest adapter probe requires Linux `epoll` and `waitid`;
158
+ it is explicitly unavailable on Darwin and remains required on every Ubuntu
159
+ Ruby job. All other deterministic examples and official addon acceptance run
160
+ natively on macOS. Pre-commit's real container integration remains required on
161
+ Linux; hosted macOS has no configured Docker/Podman engine. No native Windows
162
+ jobs are used, and WSL2 runtime validation remains a separate real-host gate.
108
163
 
109
164
  The test checkout must be writable: the QEMU monitor-directory cleanup fixture
110
165
  uses the checkout as a synthetic home. A read-only container bind mount caused one
@@ -390,8 +445,35 @@ Title and release-note edits do not repair incorrect immutable assets.
390
445
 
391
446
  Release notes use merged PR labels from `.github/release.yml`, independently of
392
447
  Conventional Commit types. See [label conventions](../CONTRIBUTING.md#release-note-labels).
393
- The first distribution is a GitHub-attached Ruby gem; RubyGems.org publication,
394
- standalone Linux/macOS binaries and automatic self-update are not implemented.
448
+ The first alpha is distributed as a verified GitHub Release gem and published
449
+ unchanged to [RubyGems.org](https://rubygems.org/gems/empeira) via OIDC Trusted
450
+ Publishing. Standalone Linux/macOS binaries and automatic self-update are not
451
+ implemented.
452
+
453
+ ### RubyGems.org distribution (manual)
454
+
455
+ After a verified GitHub Release exists, use
456
+ `.github/workflows/publish-rubygems.yml` via **Actions → Publish existing release to RubyGems.org → Run workflow**.
457
+ Run it from `main`, entering the existing release tag (for example `v0.1.0-alpha.1`).
458
+ The default `dry_run: true` checks the existing GitHub release, tag ancestry,
459
+ uploaded gem SHA-256 and embedded build metadata; it never creates a tag,
460
+ rebuilds a gem or contacts RubyGems with publishing credentials.
461
+
462
+ For a new gem that has not yet been published, the owner must first configure a
463
+ [Pending Trusted Publisher](https://rubygems.org/profile/oidc/pending_trusted_publishers)
464
+ on RubyGems.org. Empeira's publisher is already configured for gem `empeira`,
465
+ repository owner `c8m6`, repository `empeira`, workflow filename
466
+ `publish-rubygems.yml` and no GitHub Environment. After a successful dry-run,
467
+ run the same workflow with `dry_run: false`. Only its upload step receives
468
+ short-lived RubyGems credentials via GitHub OIDC; no persistent API key is required.
469
+
470
+ Publishing uploads the exact previously verified GitHub Release asset, not a
471
+ second build, and does not modify GitHub Releases or source. It fails closed
472
+ if the asset differs, the tag is not on `main`, or RubyGems rejects the upload.
473
+ RubyGems does not allow uploading the same gem name/version twice; inspect
474
+ the existing published version before rerunning a failed push. After success,
475
+ install the alpha with `gem install empeira --pre`. GitHub Releases remain
476
+ the authoritative archive of release artifacts.
395
477
 
396
478
  ## Dependency and image provenance
397
479
 
@@ -470,3 +552,8 @@ The DNS gate checks UDP/TCP, internal/external names, NXDOMAIN, large-answer fal
470
552
  unavailable/recreated CoreDNS, a second workspace with a different subnet and
471
553
  CoreDNS address, rule removal and idempotent reconciliation. Node identity also
472
554
  checks both virtual DNS transports before and after node stop/start.
555
+
556
+ The VirtIO upload regression deliberately delays staged-file unlinking. Successful
557
+ completion must follow both atomic installation and temporary-file cleanup; a
558
+ cleanup failure cannot acknowledge success. The unchanged binary-upload/empty-stage
559
+ assertions cover this ordering on Linux, independently from the real KVM gate.
data/docs/installation.md CHANGED
@@ -1,36 +1,63 @@
1
1
  # Installation
2
2
 
3
3
  Empeira runs natively on Linux and macOS. Windows uses **WSL2 → Linux → Empeira**;
4
- native Windows Ruby is unsupported. The gemspec requires **Ruby >= 3.4**; the
5
- supported Ruby versions are **3.4 and 4.0**. Normal CI tests both on Ubuntu;
6
- release gems are built with Ruby 3.4. Use source installation until a release gem
7
- is available. No RubyGems.org publication is assumed.
4
+ native Windows Ruby is unsupported. The gemspec requires **Ruby >= 3.1**; supported Ruby versions are **3.1, 3.2,
5
+ 3.3, 3.4 and 4.0**. Normal CI tests all five on Ubuntu and native macOS;
6
+ release gems are built with Ruby 3.4. The first public alpha is
7
+ [`v0.1.0-alpha.1`](https://github.com/c8m6/empeira/releases/tag/v0.1.0-alpha.1)
8
+ and is available on [RubyGems.org](https://rubygems.org/gems/empeira).
9
+
10
+ ## Install from RubyGems.org (recommended)
11
+
12
+ After installing Ruby 3.1+ and the appropriate host tools/container runtime
13
+ described below, install Empeira from RubyGems.org:
14
+
15
+ ```bash
16
+ gem install --user-install empeira --pre
17
+ export PATH="$(ruby -r rubygems -e 'puts Gem.user_dir')/bin:$PATH"
18
+ empeira --version
19
+ empeira help
20
+ ```
21
+
22
+ The `--pre` flag is needed while Empeira is available as a prerelease. For an
23
+ exact installation of the first alpha, use:
24
+
25
+ ```bash
26
+ gem install --user-install empeira --version 0.1.0.pre.alpha.1
27
+ ```
28
+
29
+ A gem installation needs neither Bundler nor a Git checkout of Empeira. It
30
+ installs the `thor` runtime dependency automatically; it does **not** install
31
+ Git, Podman/Docker, QEMU, or Puppet/OpenVox node images. Do not use `sudo gem install` for a normal user installation. Keep the gem user's `bin` directory
32
+ in your shell `PATH` to retain the command across new terminal sessions.
33
+
34
+ If you previously used the source-installation `empeira()` shell function,
35
+ remove that function from your shell configuration and start a new shell before
36
+ testing the installed executable. The source function otherwise takes precedence.
8
37
 
9
38
  ## Linux and WSL2
10
39
 
11
- These commands target **Ubuntu 24.04 LTS** and **Debian 13 trixie**. Their default
12
- Ruby packages are below Empeira's minimum; Ruby 3.2 and 3.3 are unsupported.
13
- Install Ruby 3.4+ with your established Ruby version manager before using Empeira.
14
- Ubuntu 22.04 and Debian 12 also need a newer Ruby. Installing the distribution's
15
- default `ruby` package does not satisfy the gemspec on these hosts.
40
+ These commands target **Ubuntu 24.04 LTS** and **Debian 13 trixie**. Use Ruby 3.1+ from the distribution or your established Ruby version manager.
41
+ Check `ruby --version` before installing the gem. The distribution's default
42
+ `ruby` package is suitable only when it meets the minimum; otherwise activate
43
+ a compatible Ruby before proceeding.
16
44
 
17
- Core/source development dependencies and SSH access:
45
+ Host utilities and SSH access:
18
46
 
19
47
  ```bash
20
48
  sudo apt-get update
21
- sudo apt-get install -y git build-essential openssh-client iproute2
22
- # Activate Ruby 3.4+ using your Ruby version manager before continuing.
23
- gem install bundler
49
+ sudo apt-get install -y git openssh-client iproute2
50
+ # Activate Ruby 3.1+ when the distribution's Ruby is too old.
24
51
  ruby --version
25
52
  gem --version
26
- bundle --version
27
53
  git --version
28
54
  ssh -V
29
55
  ```
30
56
 
31
- The selected Ruby installation must supply `gem` and matching development headers;
32
- `gem install bundler` supplies `bundle`. `build-essential` builds native development
33
- gems; additional Ruby build prerequisites depend on your version manager.
57
+ The selected Ruby installation must supply `gem`. Bundler, a source checkout and
58
+ a C/C++ build toolchain are not required to install the published Empeira gem.
59
+ They may be needed for source development or for setting up your Ruby environment;
60
+ see [source installation](#source-installation-and-project-opt-in).
34
61
  `openssh-client` supplies `ssh` and `ssh-keygen` for regular node SSH. VM management
35
62
  uses private VirtIO-Serial and Python 3 already present in the selected cloud image.
36
63
  `iproute2` supplies host route inspection for automatic workspace subnet allocation. Coreutils supplies
@@ -131,11 +158,8 @@ Install Homebrew using its [official instructions](https://brew.sh/), then core:
131
158
  ```bash
132
159
  brew install git ruby@3.4 openssh
133
160
  export PATH="$(brew --prefix ruby@3.4)/bin:$PATH"
134
- gem install --user-install bundler
135
- export PATH="$(ruby -r rubygems -e 'puts Gem.user_dir')/bin:$PATH"
136
161
  ruby --version
137
162
  gem --version
138
- bundle --version
139
163
  git --version
140
164
  ssh -V
141
165
  ```
@@ -189,7 +213,13 @@ not establish those results. See the [VM validation gates](development.md#vm-and
189
213
 
190
214
  ## Source installation and project opt-in
191
215
 
216
+ For development or unreleased changes, run Empeira directly from a Git checkout.
217
+ This alternative requires Bundler and the development dependencies (including a
218
+ build toolchain where native gems need it). Set up Bundler first, then:
219
+
192
220
  ```bash
221
+ gem install --user-install bundler
222
+ export PATH="$(ruby -r rubygems -e 'puts Gem.user_dir')/bin:$PATH"
193
223
  git clone https://github.com/c8m6/empeira.git
194
224
  cd empeira
195
225
  bundle config set --local path vendor/bundle
@@ -213,21 +243,22 @@ invocation and paths with spaces work. No Puppet-file heuristic or `--project`
213
243
  selector exists. The source shell function preserves the current directory.
214
244
  Version/help/self-update work outside Git (self-update currently reports unavailable).
215
245
 
216
- ## Release gem installation
246
+ ## Install the GitHub Release gem (alternative)
217
247
 
218
- Download the `.gem` asset from a [GitHub release](https://github.com/c8m6/empeira/releases)
219
- when one is available, then install it with your existing Ruby environment:
248
+ The [GitHub Releases](https://github.com/c8m6/empeira/releases) page also
249
+ provides the exact `.gem` asset built and verified by the release pipeline.
250
+ For example, after downloading the first alpha:
220
251
 
221
252
  ```bash
222
- gem install --user-install ./empeira-VERSION.gem
253
+ gem install --user-install ./empeira-0.1.0.pre.alpha.1.gem
223
254
  export PATH="$(ruby -r rubygems -e 'puts Gem.user_dir')/bin:$PATH"
224
255
  empeira --version
225
256
  ```
226
257
 
227
- Replace `VERSION` with the downloaded asset's filename. RubyGems installs the Thor
228
- runtime dependency; no third-party agents, images or binaries are bundled. The
229
- release version is embedded in BuildInfo. Standalone binaries and automatic
230
- self-update are unavailable. Do not use `sudo gem install` for normal operation.
258
+ Both distribution paths use the same verified gem for the corresponding release.
259
+ The release version is embedded in BuildInfo. No third-party agents, images or
260
+ binaries are bundled. Standalone platform binaries and automatic self-update
261
+ remain unavailable.
231
262
 
232
263
  For other Linux distributions, install equivalent Ruby/Git/runtime packages through
233
264
  your established host setup. Guest OS support does not imply a tested host recipe.
@@ -1,5 +1,5 @@
1
1
  {
2
- "version": "0.1.0-alpha.1",
3
- "revision": "7cd2d71ea0b7cfa7008f8faf18e975499c5af9c0",
4
- "build_time": "2026-10-14T09:19:05Z"
2
+ "version": "0.1.0-alpha.3",
3
+ "revision": "bfada39f711fd74dae3f061f80e1c5293bd13eec",
4
+ "build_time": "2026-10-15T13:10:56Z"
5
5
  }
@@ -0,0 +1,61 @@
1
+ # frozen_string_literal: true
2
+
3
+ # Ruby 3.2 introduced immutable Data value objects. Keep Ruby 3.1 support
4
+ # without replacing the native Data implementation on newer Rubies.
5
+ module Empeira
6
+ module Compatibility
7
+ module DataFactory
8
+ module_function
9
+
10
+ # rubocop:disable-next Metrics/AbcSize, Metrics/MethodLength -- Ruby 3.1 must emulate Data initializer semantics.
11
+ def define(*members, &definition)
12
+ # The explicit nil class name also permits zero members and preserves
13
+ # Struct's value equality, hashing and pattern matching for empty records.
14
+ base_class = Struct.new(nil, *members, keyword_init: true)
15
+ base_class.class_eval do
16
+ def initialize(*positional, **keywords)
17
+ fields = self.class.members
18
+ raise ArgumentError, 'too many positional arguments' if positional.size > fields.size
19
+
20
+ supplied = fields.take(positional.size).zip(positional).to_h.merge(keywords)
21
+ missing = fields - supplied.keys
22
+ unexpected = supplied.keys - fields
23
+ raise ArgumentError, "missing keywords: #{missing.join(', ')}" unless missing.empty?
24
+ raise ArgumentError, "unknown keywords: #{unexpected.join(', ')}" unless unexpected.empty?
25
+
26
+ super(**supplied)
27
+ freeze
28
+ end
29
+
30
+ def with(**changes)
31
+ self.class.new(**to_h, **changes)
32
+ end
33
+
34
+ def initialize_copy(other)
35
+ super
36
+ freeze
37
+ end
38
+ end
39
+ value_class = Class.new(base_class)
40
+ define_constructors(value_class)
41
+ value_class.include(self)
42
+ value_class.class_eval(&definition) if definition
43
+ value_class
44
+ end
45
+
46
+ # rubocop:disable-next Metrics/AbcSize -- Normalize native Data constructors before custom initialization.
47
+ def define_constructors(value_class)
48
+ value_class.define_singleton_method(:new) do |*positional, **keywords|
49
+ raise ArgumentError, 'cannot mix positional and keyword arguments' unless positional.empty? || keywords.empty?
50
+ raise ArgumentError, 'too many positional arguments' if positional.size > members.size
51
+
52
+ keywords = members.take(positional.size).zip(positional).to_h unless positional.empty?
53
+ super(**keywords).freeze
54
+ end
55
+ value_class.define_singleton_method(:[]) { |*positional, **keywords| new(*positional, **keywords) }
56
+ end
57
+ end
58
+ end
59
+ end
60
+
61
+ Data = Empeira::Compatibility::DataFactory unless defined?(Data)
@@ -6,7 +6,7 @@ module Empeira
6
6
  class Backend
7
7
  attr_reader :context, :runtime, :runner, :store
8
8
 
9
- def self.build(context:, **)
9
+ def self.build(context:, **options)
10
10
  klass = if context.container_engine == 'docker'
11
11
  DockerAdapter
12
12
  elsif context.platform.os == :macos
@@ -14,7 +14,7 @@ module Empeira
14
14
  else
15
15
  LinuxPodman
16
16
  end
17
- klass.new(context: context, **)
17
+ klass.new(context: context, **options)
18
18
  end
19
19
 
20
20
  def initialize(context:, runtime:, runner:, store:)
@@ -92,8 +92,8 @@ module Empeira
92
92
  raise Providers::OwnershipError, 'VM TAP already exists; inspect its owning process'
93
93
  end
94
94
 
95
- def namespace(*, **)
96
- command('podman', '--remote=false', 'unshare', '--rootless-netns', *, **)
95
+ def namespace(*arguments, **options)
96
+ command('podman', '--remote=false', 'unshare', '--rootless-netns', *arguments, **options)
97
97
  end
98
98
 
99
99
  def attached?(record, links)
@@ -98,13 +98,13 @@ module Empeira
98
98
  raise Error, 'Podman Machine cannot verify the peer helper at its host path; configure its shared mount'
99
99
  end
100
100
 
101
- def namespace(*)
102
- executable, *arguments = namespace_command(@machine, *)
103
- command(executable, *arguments)
101
+ def namespace(*arguments)
102
+ executable, *command_arguments = namespace_command(@machine, *arguments)
103
+ command(executable, *command_arguments)
104
104
  end
105
105
 
106
- def namespace_command(machine, *)
107
- ['podman', 'machine', 'ssh', machine, Shellwords.join(['podman', 'unshare', '--rootless-netns', *])]
106
+ def namespace_command(machine, *arguments)
107
+ ['podman', 'machine', 'ssh', machine, Shellwords.join(['podman', 'unshare', '--rootless-netns', *arguments])]
108
108
  end
109
109
  end
110
110
  end
@@ -5,10 +5,10 @@ module Empeira
5
5
  class Definition < Services::Definition
6
6
  attr_reader :hostname
7
7
 
8
- def initialize(hostname:, workspace:, **)
8
+ def initialize(hostname:, workspace:, **options)
9
9
  @hostname = hostname
10
10
  super(key: "node-#{Digest::SHA256.hexdigest(hostname)[0, 24]}", workspace: workspace,
11
- hostname: hostname, **)
11
+ hostname: hostname, **options)
12
12
  end
13
13
 
14
14
  def ownership_labels
@@ -73,9 +73,9 @@ module Empeira
73
73
  { label: 'VM network helper', image: adapter.image, recipe: adapter.recipe, files: adapter.files }]
74
74
  end
75
75
 
76
- def utility_artifact(key, label, **)
76
+ def utility_artifact(key, label, **options)
77
77
  Empeira::Images::Configuration.artifact(
78
- @config.dig('images', key), registry: @config.dig('images', 'registry'), **
78
+ @config.dig('images', key), registry: @config.dig('images', 'registry'), **options
79
79
  ).merge(label: label)
80
80
  end
81
81
 
data/lib/empeira.rb CHANGED
@@ -1,5 +1,6 @@
1
1
  # frozen_string_literal: true
2
2
 
3
+ require_relative 'empeira/compatibility/data'
3
4
  require_relative 'empeira/errors'
4
5
  require_relative 'empeira/progress'
5
6
  require_relative 'empeira/build_info'
@@ -5,7 +5,7 @@ RUN apk add --no-cache git ca-certificates
5
5
  WORKDIR /opt/empeira-modules
6
6
  COPY Gemfile Gemfile.lock ./
7
7
  RUN apk add --no-cache --virtual .build-deps build-base libffi-dev \
8
- && gem install bundler -v 4.0.22 --no-document \
8
+ && gem install bundler -v 2.6.9 --no-document \
9
9
  && bundle config set deployment true && bundle install --jobs 2 \
10
10
  && apk del .build-deps
11
11
  COPY worker.rb ./
@@ -3,3 +3,9 @@
3
3
  source 'https://rubygems.org'
4
4
 
5
5
  gem 'r10k', '5.0.3'
6
+
7
+ # r10k's newer fast_gettext 4.x dependency requires Ruby >= 3.2.
8
+ # Keep the module helper usable with the oldest supported Ruby (3.1).
9
+ gem 'fast_gettext', '~> 3.1.0'
10
+ # Newer multi_json requires Ruby >= 3.2.
11
+ gem 'multi_json', '1.18.0'
@@ -13,9 +13,8 @@ GEM
13
13
  faraday (>= 1, < 3)
14
14
  faraday-net_http (3.4.4)
15
15
  net-http (~> 0.5)
16
- fast_gettext (4.1.1)
16
+ fast_gettext (3.1.0)
17
17
  prime
18
- racc
19
18
  fiddle (1.1.8)
20
19
  forwardable (1.4.0)
21
20
  gettext (3.5.2)
@@ -36,7 +35,7 @@ GEM
36
35
  log4r (1.1.10)
37
36
  logger (1.7.0)
38
37
  minitar (1.1.0)
39
- multi_json (1.21.2)
38
+ multi_json (1.18.0)
40
39
  net-http (0.9.1)
41
40
  uri (>= 0.11.1)
42
41
  prime (0.1.4)
@@ -68,18 +67,19 @@ PLATFORMS
68
67
  x86_64-linux
69
68
 
70
69
  DEPENDENCIES
70
+ fast_gettext (~> 3.1.0)
71
+ multi_json (= 1.18.0)
71
72
  r10k (= 5.0.3)
72
73
 
73
74
  CHECKSUMS
74
75
  base64 (0.3.0) sha256=27337aeabad6ffae05c265c450490628ef3ebd4b67be58257393227588f5a97b
75
- bundler (4.0.22) sha256=d8d5ec84c8555e0af71db63ed7aee4d1a8fb839ec46d84212d61979242a5d75a
76
76
  colored2 (4.0.3) sha256=63e1038183976287efc43034f5cca17fb180b4deef207da8ba78d051cbce2b37
77
77
  cri (2.15.12) sha256=8abfe924ef53e772a8e4ee907e791d3bfcfca78bc62a5859e3b9899ba29956e5
78
78
  erubi (1.13.1) sha256=a082103b0885dbc5ecf1172fede897f9ebdb745a4b97a5e8dc63953db1ee4ad9
79
79
  faraday (2.14.4) sha256=9bb4408c44621b0dbaaaa39e5dbfc4dbe58cc6751b6eb94d16ae9e1f2f2fab6e
80
80
  faraday-follow_redirects (0.5.0) sha256=5cde93c894b30943a5d2b93c2fe9284216a6b756f7af406a1e55f211d97d10ad
81
81
  faraday-net_http (3.4.4) sha256=0e78af151747ed1b00f33e25973b4bc220d7f16c00c39676817c8b12331eb588
82
- fast_gettext (4.1.1) sha256=e8c532f6945b0dc31efb2e91366bd56630bc3ae6a05a4126937124e40366efd8
82
+ fast_gettext (3.1.0) sha256=9c4c84c33a875cc9edc062179229f9d373179a624d3c961eed747cddbab72ac5
83
83
  fiddle (1.1.8) sha256=7fa8ee3627271497f3add5503acdbc3f40b32f610fc1cf49634f083ef3f32eee
84
84
  forwardable (1.4.0) sha256=f1cd40cc9812937980e1c76f1aa053660990a7c9b6a98fc37d945468afcce838
85
85
  gettext (3.5.2) sha256=ada02c59aa7e9f56bd2522faedaed16421dd2f3ddb5fe28628c0be5abcbf3c74
@@ -90,7 +90,7 @@ CHECKSUMS
90
90
  log4r (1.1.10) sha256=9b452928c964b7c54c09aeb25ff045b5a722b387b16c9ce37cb1baec00062966
91
91
  logger (1.7.0) sha256=196edec7cc44b66cfb40f9755ce11b392f21f7967696af15d274dde7edff0203
92
92
  minitar (1.1.0) sha256=38db0cfb6f3801017946cdcd8dc53f2cf3fd41ff752892312bf9a1639c9ea23e
93
- multi_json (1.21.2) sha256=245531eaf54bdba57aba515ff08b4503505c5e09911daa89c5a561e2f85bea8e
93
+ multi_json (1.18.0) sha256=23f70cf73e2895a33e6d10f0ec955b28ef5627ca4bbad0f292e7d16f4675596f
94
94
  net-http (0.9.1) sha256=25ba0b67c63e89df626ed8fac771d0ad24ad151a858af2cc8e6a716ca4336996
95
95
  prime (0.1.4) sha256=4d755ebf7c2994a6f3a3fee0d072063be3fff2d4042ebff6cd5eebd4747a225e
96
96
  puppet_forge (6.2.0) sha256=9cde4841a7a6950afeb0c4fec02449931179863918c0a6e6909cb2a6c6998a0c
@@ -102,4 +102,4 @@ CHECKSUMS
102
102
  uri (1.1.1) sha256=379fa58d27ffb1387eaada68c749d1426738bd0f654d812fcc07e7568f5c57c6
103
103
 
104
104
  BUNDLED WITH
105
- 4.0.22
105
+ 2.6.9
@@ -316,6 +316,7 @@ def handle(message):
316
316
  active['file'].close()
317
317
  active = None
318
318
  pending = None
319
+ status = 0
319
320
  try:
320
321
  with tempfile.NamedTemporaryFile(dir=os.path.dirname(destination), delete=False) as target:
321
322
  pending = target.name
@@ -326,14 +327,16 @@ def handle(message):
326
327
  os.fsync(target.fileno())
327
328
  os.replace(pending, destination)
328
329
  pending = None
329
- send({'type': 'exit', 'status': 0, 'timed_out': False})
330
330
  except OSError as error:
331
331
  send({'type': 'stderr', 'data': base64.b64encode((os.strerror(error.errno) + '\n').encode()).decode()})
332
- send({'type': 'exit', 'status': 1, 'timed_out': False})
332
+ status = 1
333
333
  finally:
334
334
  os.unlink(staged)
335
335
  if pending:
336
336
  os.unlink(pending)
337
+ # Completion includes staging cleanup; an unlink failure must never
338
+ # acknowledge success to a controller that is already enrolling/Puppet.
339
+ send({'type': 'exit', 'status': status, 'timed_out': False})
337
340
  else:
338
341
  raise ValueError('Unexpected protocol operation')
339
342
 
@@ -1,10 +1,7 @@
1
1
  ARG EMPEIRA_BASE_UBUNTU=library/ubuntu@sha256:da6fc2be547864451aa253836dd926da33623312df4a9a243e35dc877c378a78
2
2
  FROM ${EMPEIRA_BASE_UBUNTU}
3
3
  ENV DEBIAN_FRONTEND=noninteractive
4
- RUN ["apt-get", "-o", "APT::Update::Error-Mode=any", "update"]
5
- RUN ["apt-get", "install", "--yes", "--no-install-recommends", "ca-certificates"]
6
- RUN ["sed", "-i", "/Types: deb/a Snapshot: yes", "/etc/apt/sources.list.d/ubuntu.sources"]
7
- RUN ["apt-get", "--snapshot", "20261001T000000Z", "-o", "APT::Update::Error-Mode=any", "update"]
8
- RUN ["apt-get", "--snapshot", "20261001T000000Z", "install", "--yes", "--no-install-recommends", "squid=7.2-2ubuntu2.2"]
9
- RUN ["apt-get", "clean"]
4
+ RUN apt-get -o APT::Update::Error-Mode=any update \
5
+ && apt-get install --yes --no-install-recommends ca-certificates squid \
6
+ && rm -rf /var/lib/apt/lists/*
10
7
  USER proxy
metadata CHANGED
@@ -1,7 +1,7 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: empeira
3
3
  version: !ruby/object:Gem::Version
4
- version: 0.1.0.pre.alpha.1
4
+ version: 0.1.0.pre.alpha.3
5
5
  platform: ruby
6
6
  authors:
7
7
  - Christian Meißner (c8m6)
@@ -72,6 +72,7 @@ files:
72
72
  - lib/empeira/cli/progress.rb
73
73
  - lib/empeira/cli/proxy.rb
74
74
  - lib/empeira/cli/update.rb
75
+ - lib/empeira/compatibility/data.rb
75
76
  - lib/empeira/completion/addons.rb
76
77
  - lib/empeira/completion/bash.rb
77
78
  - lib/empeira/completion/catalog.rb
@@ -313,7 +314,7 @@ required_ruby_version: !ruby/object:Gem::Requirement
313
314
  requirements:
314
315
  - - ">="
315
316
  - !ruby/object:Gem::Version
316
- version: '3.4'
317
+ version: '3.1'
317
318
  required_rubygems_version: !ruby/object:Gem::Requirement
318
319
  requirements:
319
320
  - - ">="