feat: Windows display name, clean-reinstall, macOS ensure-hosts, 3-OS service smoke CI#34
Merged
Merged
Conversation
Comment on lines
+35
to
+163
| name: install/start/reinstall/uninstall (${{ matrix.os }}) | ||
| strategy: | ||
| fail-fast: false | ||
| matrix: | ||
| os: [windows-latest, macos-14, ubuntu-latest] | ||
| runs-on: ${{ matrix.os }} | ||
| steps: | ||
| - uses: actions/checkout@v4 | ||
| with: | ||
| persist-credentials: false | ||
|
|
||
| - name: Install Rust | ||
| uses: dtolnay/rust-toolchain@stable | ||
|
|
||
| - uses: Swatinem/rust-cache@v2 | ||
| with: | ||
| key: service-smoke | ||
|
|
||
| - name: Build dig-node (release) | ||
| run: cargo build --release --locked --bin dig-node | ||
|
|
||
| # `dig-node install` registers a USER-level systemd unit on Linux (no root needed), which | ||
| # needs a real user D-Bus session — the GH-hosted runner's non-interactive job shell does | ||
| # not start one implicitly. `enable-linger` + pointing at the per-uid runtime dir brings one | ||
| # up so `systemctl --user ...` (what service-manager shells out to) has a bus to talk to. | ||
| - name: (Linux) bring up a user systemd/D-Bus session | ||
| if: runner.os == 'Linux' | ||
| run: | | ||
| sudo loginctl enable-linger "$(whoami)" | ||
| echo "XDG_RUNTIME_DIR=/run/user/$(id -u)" >> "$GITHUB_ENV" | ||
|
|
||
| - name: Resolve the built binary path | ||
| id: bin | ||
| shell: bash | ||
| run: | | ||
| if [ "${{ runner.os }}" = "Windows" ]; then | ||
| echo "path=target/release/dig-node.exe" >> "$GITHUB_OUTPUT" | ||
| else | ||
| echo "path=target/release/dig-node" >> "$GITHUB_OUTPUT" | ||
| fi | ||
|
|
||
| - name: Install (1st) — fresh registration | ||
| shell: bash | ||
| run: | | ||
| "${{ steps.bin.outputs.path }}" install --json | tee install-1.json | ||
| grep -q '"installed":true' install-1.json | ||
| grep -q '"reinstalled":false' install-1.json | ||
|
|
||
| - name: Windows — assert the SCM display name | ||
| if: runner.os == 'Windows' | ||
| shell: bash | ||
| run: | | ||
| sc.exe qc net.dignetwork.dig-node | tee sc-qc-1.txt | ||
| grep -Eq 'DISPLAY_NAME[[:space:]]*: DIG NETWORK: NODE' sc-qc-1.txt | ||
|
|
||
| - name: Start + poll for RUNNING | ||
| shell: bash | ||
| run: | | ||
| "${{ steps.bin.outputs.path }}" start --json || true | ||
| ok=0 | ||
| for _ in $(seq 1 20); do | ||
| if "${{ steps.bin.outputs.path }}" status --json | tee status-1.json | grep -q '"serving":true'; then | ||
| ok=1 | ||
| break | ||
| fi | ||
| sleep 1 | ||
| done | ||
| test "$ok" -eq 1 | ||
|
|
||
| - name: Install (2nd) — proves clean-reinstall avoids CreateService 1073 | ||
| shell: bash | ||
| run: | | ||
| "${{ steps.bin.outputs.path }}" install --json | tee install-2.json | ||
| grep -q '"installed":true' install-2.json | ||
| grep -q '"reinstalled":true' install-2.json | ||
|
|
||
| - name: Windows — re-assert the SCM display name after reinstall | ||
| if: runner.os == 'Windows' | ||
| shell: bash | ||
| run: | | ||
| sc.exe qc net.dignetwork.dig-node | tee sc-qc-2.txt | ||
| grep -Eq 'DISPLAY_NAME[[:space:]]*: DIG NETWORK: NODE' sc-qc-2.txt | ||
|
|
||
| - name: Start again + poll for RUNNING | ||
| shell: bash | ||
| run: | | ||
| "${{ steps.bin.outputs.path }}" start --json || true | ||
| ok=0 | ||
| for _ in $(seq 1 20); do | ||
| if "${{ steps.bin.outputs.path }}" status --json | tee status-2.json | grep -q '"serving":true'; then | ||
| ok=1 | ||
| break | ||
| fi | ||
| sleep 1 | ||
| done | ||
| test "$ok" -eq 1 | ||
|
|
||
| - name: Stop | ||
| shell: bash | ||
| run: | | ||
| "${{ steps.bin.outputs.path }}" stop --json | ||
|
|
||
| - name: Uninstall + assert the service is gone | ||
| shell: bash | ||
| run: | | ||
| "${{ steps.bin.outputs.path }}" uninstall --json | tee uninstall.json | ||
| grep -q '"installed":false' uninstall.json | ||
| if [ "${{ runner.os }}" = "Windows" ]; then | ||
| ! sc.exe query net.dignetwork.dig-node | ||
| elif [ "${{ runner.os }}" = "macOS" ]; then | ||
| ! launchctl print "gui/$(id -u)/net.dignetwork.dig-node" | ||
| else | ||
| ! systemctl --user cat net.dignetwork.dig-node.service | ||
| fi | ||
|
|
||
| - name: Upload diagnostics | ||
| if: always() | ||
| uses: actions/upload-artifact@v4 | ||
| with: | ||
| name: service-smoke-${{ matrix.os }} | ||
| path: | | ||
| install-1.json | ||
| install-2.json | ||
| status-1.json | ||
| status-2.json | ||
| uninstall.json | ||
| sc-qc-1.txt | ||
| sc-qc-2.txt | ||
| if-no-files-found: ignore |
…sts, 3-OS smoke CI - install now clean-reinstalls (stop -> delete -> wait -> create) instead of a bare create, so re-running it against an already-registered service no longer hits Windows CreateService error 1073. It deliberately never auto-starts (unlike the dig-dns pattern this mirrors), since the dig-installer issues its own start afterward and treats a start failure as fatal for that step. - Windows: sc config sets the SCM display name to "DIG NETWORK: NODE" after create, then sc qc reads it back to confirm the override actually took (result.display_name_verified in --json). Verified live in CI: sc qc reports DISPLAY_NAME: DIG NETWORK: NODE and display_name_verified:true. - The existing-service probe uses the identifier service-manager's OWN backend actually registers under: to_qualified_name() for Windows/launchd, but to_script_name() for systemd, whose unit-file naming diverges silently. Caught by a real ubuntu-latest smoke run (a second install reported reinstalled:false until fixed); regression test added. - macOS postinstall now calls the binary's own idempotent `ensure-hosts` so dig.local -> 127.0.0.2 is registered on .pkg install (Windows/.deb already did this). - New service-smoke CI job (windows-latest/macos-14/ubuntu-latest): build, install, start, poll for serving, assert the Windows display name, install a second time to prove no 1073, stop, uninstall, and assert the registration is gone. All three legs green against the fix. - config::DEFAULT_PORT now sources dig_constants::DIG_NODE_PORT (single-sourced §5.3 default) via a rev-pinned dig-constants 0.3.0 dependency, kept a cargo- distinct source from dig-node-core's bare-git 0.2.x pin so the P2P crate chain (dig-nat/dig-gossip/dig-dht/dig-onion) does not need to move in this PR. - SPEC.md: document the display-name + clean-reinstall contract (#494), and the previously-undocumented 9444 DIG_PEER_PORT P2P listener default. - DEVELOPMENT_LOG.md: durable realizations on the bare-git version-unification gotcha, the installer's install->start sequencing constraint, and the systemd script-name vs qualified-name divergence. Minor version bump (0.28.0 -> 0.29.0): new capability, no breaking change. Closes #494, refs #502. Co-Authored-By: Claude <noreply@anthropic.com>
21ae332 to
db95aed
Compare
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Summary
Hardens the OS-service install path across Windows/macOS/Linux (#494, #502).
installis now stop -> delete -> wait -> create (never a bare create),so re-running it against an already-registered service no longer hits Windows
CreateServiceerror 1073. Deliberately never auto-starts the service (unlike the
dig-dnspattern thismirrors): the dig-installer issues its own
startafterward and treats astartfailure asfatal for that step, so an auto-started
installcould flip its reported outcome to failed.sc configsets the SCM display name to "DIG NETWORK: NODE" aftercreate, then
sc qcreads it back to confirm the override actually took(
result.display_name_verifiedin--json). Verified LIVE in CI (see below).to_qualified_name()uniformly, butservice-manager's systemd backend actually registersunder
to_script_name()instead (a silent divergence with no compile-time signal). This madeis_installed()always reportfalseon Linux, defeating clean-reinstall there. Fixed +regression-tested.
ensure-hosts—postinstallnow calls the binary's own idempotentensure-hostssodig.local->127.0.0.2is registered on.pkginstall (Windows MSI + the.debalready didthis).
service-smokeCI job (windows-latest/macos-14/ubuntu-latest): build -> install ->start -> poll for serving -> assert the Windows display name -> install a SECOND time (proves
no 1073) -> re-assert the display name -> start -> poll -> stop -> uninstall -> assert the
registration is gone.
config::DEFAULT_PORTnow sourcesdig_constants::DIG_NODE_PORT(single-sourced §5.3 default)via a rev-pinned
dig-constants0.3.0 dependency — kept a cargo-DISTINCT source fromdig-node-core's bare-git 0.2.x pin so the P2P crate chain (dig-nat/dig-gossip/dig-dht/dig-onion)does not need to move in this PR.
SPEC.mddocuments the display-name + clean-reinstall contract and the previously-undocumented9444DIG_PEER_PORTP2P listener default;DEVELOPMENT_LOG.mdrecords the durable gotchasfound along the way (bare-git version unification, the installer's install->start sequencing
constraint, the systemd script-name divergence).
Minor version bump: 0.28.0 -> 0.29.0 (new capability, no breaking change).
Blast radius checked (gitnexus-style manual analysis — MCP tool unavailable this session)
service::SERVICE_LABEL— only consumer iswin_service.rs(SCM dispatcher registration);value unchanged.
service::install/uninstall/start/stop/status— only consumer ismain.rs's CLI dispatch;signatures unchanged (
io::Result<Outcome>), somain.rsneeded no changes.config::DEFAULT_PORT— confined toconfig.rs(default +from_env+ tests); no other filereferences it.
dig-installer'sregister_dig_node/install_servicesequencing was the reasoninstallintentionally does NOT auto-start (see the module doc + DEVELOPMENT_LOG.md) — no codechange needed there since the existing "install, then separately start" sequence still works.
Test plan
cargo test -p dig-node-service --lib— 154 passed (was 153; +1 regression test), incl. newservice::testsfor the clean-reinstall mock, the sc-qc display-name read-back parser, and thesystemd script-name-vs-qualified-name regression.
cargo test -p dig-node-service --test server— 45 passed (integration suite unaffected).cargo fmt --all -- --check/cargo clippy -p dig-node-service --all-targets --locked -- -D warnings— clean (also exercises the#[cfg(windows)]branches directly, since this session runs on Windows).db95aed):install/start/reinstall/uninstallon windows-latest / macos-14 / ubuntu-latest, all pass.sc qcshowsDISPLAY_NAME : DIG NETWORK: NODE;install-2.jsoncarries"reinstalled":true,"display_name_verified":true;uninstall.jsoncarries"installed":false.install-2.jsoncarries"reinstalled":true(the 1073-equivalent clean-reinstall path exercised for real).Closes #494 (node-half), refs #502.
Co-Authored-By: Claude noreply@anthropic.com