From c4274c93fbf9d370e4ee1024d634d1fe46161690 Mon Sep 17 00:00:00 2001 From: Ana Hobden Date: Fri, 16 Dec 2022 10:55:28 -0800 Subject: [PATCH] Add action & Tune Tracing (#119) * Add action * Checkout so we have actions.yml * yaml poking * Handle GITHUB_TOKEN * Don't ask github to do templating, use directives for logging * Missing changes * Fix build error * Fix yaml even more * Add shell command * Add a wait on the socket again * Print some debugging * Use more correct env vars * Correct install url logic * Use different style for inputs * Fix yaml errror * Tweak around local-root * provision nix-install.sh as well * Use nix-install.sh path directory in NIX_INSTALL_URL * Tweak variables to hopefully work * Call it BINARY_ROOT instead * Add exec output * Set no-confirm * no echo * Add token to workflow * Set no-confirm properly * Add no-confirm back for uninstall * Correct some env and vars * CreateDirectory respects existing symlink * Add a few more checks to the CI * pass valid yaml... * Slightly more aggressive cleanup of /nix * Ensure steam-deck cleans /home/nix * Add steam-deck check for persistence * Canonicalize steam-deck persistence * Ensure absolute path * Inverted logic sad * python3 on mac * Add readme info and fix a extra-conf mistype * Add unsaved changes * More fine grained trace logging * Restore spans we lost in refactor * BuiltinPlanner can accept settings * Reflect feedback * Push actually working code hopefully this time * Speeling --- .github/workflows/ci.yml | 202 +++++++++++++----- README.md | 24 +++ action.yml | 199 +++++++++++++++++ nix-install.sh | 6 +- src/action/base/create_directory.rs | 28 +-- src/action/base/create_file.rs | 40 ++-- src/action/base/create_group.rs | 20 +- src/action/base/create_or_append_file.rs | 37 ++-- src/action/base/create_user.rs | 26 ++- src/action/base/fetch_and_unpack_nix.rs | 20 +- src/action/base/move_unpacked_nix.rs | 21 +- src/action/base/setup_default_profile.rs | 17 +- src/action/common/configure_nix.rs | 69 +++++- src/action/common/configure_shell_profile.rs | 11 +- src/action/common/create_nix_tree.rs | 6 + src/action/common/create_users_and_groups.rs | 35 +-- .../common/place_channel_configuration.rs | 22 +- src/action/common/place_nix_configuration.rs | 6 + src/action/common/provision_nix.rs | 6 + src/action/darwin/bootstrap_apfs_volume.rs | 17 +- src/action/darwin/create_apfs_volume.rs | 23 +- src/action/darwin/create_nix_volume.rs | 14 +- src/action/darwin/create_synthetic_objects.rs | 9 +- src/action/darwin/enable_ownership.rs | 17 +- src/action/darwin/encrypt_apfs_volume.rs | 9 + .../darwin/kickstart_launchctl_service.rs | 17 +- src/action/darwin/unmount_apfs_volume.rs | 20 +- .../linux/configure_nix_daemon_service.rs | 6 + src/action/linux/start_systemd_unit.rs | 17 +- src/action/mod.rs | 26 ++- src/action/stateful.rs | 48 ++++- src/cli/arg/instrumentation.rs | 16 +- src/cli/subcommand/install.rs | 9 +- src/cli/subcommand/uninstall.rs | 2 +- src/planner/linux/multi.rs | 3 +- src/planner/linux/steam_deck.rs | 11 + src/planner/mod.rs | 12 +- src/settings.rs | 56 +++-- 38 files changed, 882 insertions(+), 245 deletions(-) create mode 100644 action.yml diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index 7b3aa09..315852d 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -5,14 +5,6 @@ on: push: branches: [main] -env: - NIX_INSTALL_FORCE_ALLOW_HTTP: "1" - NIX_INSTALL_UPDATE_ROOT: "http://0.0.0.0:8000" - RUST_BACKTRACE: "full" - HARMONIC_VERBOSITY: "2" - HARMONIC_NO_CONFIRM: "true" - HARMONIC_LOGGER: "pretty" - jobs: lints: name: Lints @@ -37,12 +29,6 @@ jobs: run: nix develop --store ~/.ci-store --command check-nixpkgs-fmt - name: Check EditorConfig conformance run: nix develop --store ~/.ci-store --command check-editorconfig - - name: Create artifact for `nix-install.sh` - uses: actions/upload-artifact@v3 - with: - name: nix-install - path: | - nix-install.sh build-x86_64-linux: name: Build x86_64 Linux @@ -102,29 +88,54 @@ jobs: runs-on: ubuntu-22.04 needs: [build-x86_64-linux, lints] steps: + - uses: actions/checkout@v3 - run: sudo apt install fish zsh - uses: actions/download-artifact@v3 with: name: harmonic-x86_64-linux - - uses: actions/download-artifact@v3 - with: - name: nix-install - name: Move & set executable run: | chmod +x ./harmonic - mv harmonic harmonic-x86_64-linux + mkdir install-root + cp nix-install.sh install-root/nix-install.sh + mv harmonic install-root/harmonic-x86_64-linux - name: Initial install - run: | - python -m http.server --bind 0.0.0.0 8000 & - timeout 20 bash -c 'until printf "" 2>>/dev/null >>/dev/tcp/localhost/8000; do echo "Waiting for server..."; sleep 1; done' - curl -L http://0.0.0.0:8000/nix-install.sh | sh -s -- install linux-multi --extra-conf "access-tokens = github.com=${{ secrets.GITHUB_TOKEN }}" + uses: ./ + with: + local-root: install-root/ + logger: pretty + log-directives: harmonic=trace + backtrace: full + github-token: ${{ secrets.GITHUB_TOKEN }} - name: Initial uninstall (without a `nix run` first) run: sudo -E /nix/harmonic uninstall - - name: Repeated install + env: + HARMONIC_NO_CONFIRM: true + HARMONIC_LOGGER: pretty + HARMONIC_LOG_DIRECTIVES: harmonic=trace + RUST_BACKTRACE: full + - name: Ensure `nix` is removed run: | - python -m http.server --bind 0.0.0.0 8000 & - timeout 20 bash -c 'until printf "" 2>>/dev/null >>/dev/tcp/localhost/8000; do echo "Waiting for server..."; sleep 1; done' - curl -L http://0.0.0.0:8000/nix-install.sh | sh -s -- install linux-multi --extra-conf "access-tokens = github.com=${{ secrets.GITHUB_TOKEN }}" + if systemctl is-active nix-daemon.socket; then + echo "nix-daemon.socket was still running" + exit 1 + fi + if systemctl is-active nix-daemon.service; then + echo "nix-daemon.service was still running" + exit 1 + fi + if [ -e /nix ]; then + echo "/nix exists" + exit 1 + fi + - name: Repeated install + uses: ./ + with: + local-root: install-root/ + logger: pretty + log-directives: harmonic=trace + backtrace: full + github-token: ${{ secrets.GITHUB_TOKEN }} - name: echo $PATH run: echo $PATH - name: Test `nix` with `$GITHUB_PATH` @@ -148,23 +159,42 @@ jobs: shell: fish --login {0} - name: Repeated uninstall run: sudo -E /nix/harmonic uninstall + env: + HARMONIC_NO_CONFIRM: true + HARMONIC_LOGGER: pretty + HARMONIC_LOG_DIRECTIVES: harmonic=trace + RUST_BACKTRACE: full + - name: Ensure `nix` is removed + run: | + if systemctl is-active nix-daemon.socket; then + echo "nix-daemon.socket was still running" + exit 1 + fi + if systemctl is-active nix-daemon.service; then + echo "nix-daemon.service was still running" + exit 1 + fi + if [ -e /nix ]; then + echo "/nix exists" + exit 1 + fi run-steam-deck: name: Run Steam Deck (mock) runs-on: ubuntu-22.04 needs: [build-x86_64-linux, lints] steps: + - uses: actions/checkout@v3 - run: sudo apt install fish zsh - uses: actions/download-artifact@v3 with: name: harmonic-x86_64-linux - - uses: actions/download-artifact@v3 - with: - name: nix-install - name: Move & set executable run: | chmod +x ./harmonic - mv harmonic harmonic-x86_64-linux + mkdir install-root + cp nix-install.sh install-root/nix-install.sh + mv harmonic install-root/harmonic-x86_64-linux - name: Make the CI look like a steam deck run: | mkdir -p ~/bin @@ -172,17 +202,50 @@ jobs: sudo chmod +x /bin/steamos-readonly sudo useradd -m deck - name: Initial install - run: | - python -m http.server --bind 0.0.0.0 8000 & - timeout 20 bash -c 'until printf "" 2>>/dev/null >>/dev/tcp/localhost/8000; do echo "Waiting for server..."; sleep 1; done' - curl -L http://0.0.0.0:8000/nix-install.sh | sh -s -- install steam-deck --persistence `pwd`/ci-test-nix --extra-conf "access-tokens = github.com=${{ secrets.GITHUB_TOKEN }}" + uses: ./ + with: + local-root: install-root/ + logger: pretty + log-directives: harmonic=trace + backtrace: full + github-token: ${{ secrets.GITHUB_TOKEN }} + planner: steam-deck + extra-args: --persistence /home/runner/.ci-test-nix-home - name: Initial uninstall (without a `nix run` first) run: sudo -E /nix/harmonic uninstall - - name: Repeated install + env: + HARMONIC_NO_CONFIRM: true + HARMONIC_LOGGER: pretty + HARMONIC_LOG_DIRECTIVES: harmonic=trace + RUST_BACKTRACE: full + - name: Ensure `nix` is removed run: | - python -m http.server --bind 0.0.0.0 8000 & - timeout 20 bash -c 'until printf "" 2>>/dev/null >>/dev/tcp/localhost/8000; do echo "Waiting for server..."; sleep 1; done' - curl -L http://0.0.0.0:8000/nix-install.sh | sh -s -- install steam-deck --persistence `pwd`/ci-test-nix --extra-conf "access-tokens = github.com=${{ secrets.GITHUB_TOKEN }}" + if systemctl is-active nix-daemon.socket; then + echo "nix-daemon.socket was still running" + exit 1 + fi + if systemctl is-active nix-daemon.service; then + echo "nix-daemon.service was still running" + exit 1 + fi + if [ -e /nix ]; then + echo "/nix exists" + exit 1 + fi + if [ -e /home/runner/.ci-test-nix-home ]; then + echo "/home/runner/.ci-test-nix-home exists" + exit 1 + fi + - name: Repeated install + uses: ./ + with: + local-root: install-root/ + logger: pretty + log-directives: harmonic=trace + backtrace: full + github-token: ${{ secrets.GITHUB_TOKEN }} + planner: steam-deck + extra-args: --persistence /home/runner/.ci-test-nix-home - name: echo $PATH run: echo $PATH - name: Test `nix` with `$GITHUB_PATH` @@ -206,6 +269,29 @@ jobs: shell: fish --login {0} - name: Repeated uninstall run: sudo -E /nix/harmonic uninstall + env: + HARMONIC_NO_CONFIRM: true + HARMONIC_LOGGER: pretty + HARMONIC_LOG_DIRECTIVES: harmonic=trace + RUST_BACKTRACE: full + - name: Ensure `nix` is removed + run: | + if systemctl is-active nix-daemon.socket; then + echo "nix-daemon.socket was still running" + exit 1 + fi + if systemctl is-active nix-daemon.service; then + echo "nix-daemon.service was still running" + exit 1 + fi + if [ -e /nix ]; then + echo "/nix exists" + exit 1 + fi + if [ -e /home/runner/.ci-test-nix-home ]; then + echo "/home/runner/.ci-test-nix-home exists" + exit 1 + fi build-x86_64-darwin: name: Build x86_64 Darwin @@ -231,29 +317,40 @@ jobs: runs-on: macos-12 needs: [build-x86_64-darwin, lints] steps: + - uses: actions/checkout@v3 - run: brew install fish coreutils - uses: actions/download-artifact@v3 with: name: harmonic-x86_64-darwin - - uses: actions/download-artifact@v3 - with: - name: nix-install - name: Move & set executable run: | chmod +x ./harmonic - mv harmonic harmonic-x86_64-darwin + mkdir install-root + cp nix-install.sh install-root/nix-install.sh + mv harmonic install-root/harmonic-x86_64-darwin - name: Initial install - run: | - python3 -m http.server --bind 0.0.0.0 8000 & - gtimeout 20 bash -c 'until printf "" 2>>/dev/null >>/dev/tcp/localhost/8000; do echo "Waiting for server..."; sleep 1; done' - curl -L http://0.0.0.0:8000/nix-install.sh | sh -s -- install darwin-multi --extra-conf "access-tokens = github.com=${{ secrets.GITHUB_TOKEN }}" + uses: ./ + with: + local-root: install-root/ + logger: pretty + log-directives: harmonic=trace + backtrace: full + github-token: ${{ secrets.GITHUB_TOKEN }} - name: Initial uninstall (without a `nix run` first) run: sudo -E /nix/harmonic uninstall + env: + HARMONIC_NO_CONFIRM: true + HARMONIC_LOGGER: pretty + HARMONIC_LOG_DIRECTIVES: harmonic=trace + RUST_BACKTRACE: full - name: Repeated install - run: | - python -m http.server --bind 0.0.0.0 8000 & - timeout 20 bash -c 'until printf "" 2>>/dev/null >>/dev/tcp/localhost/8000; do echo "Waiting for server..."; sleep 1; done' - curl -L http://0.0.0.0:8000/nix-install.sh | sh -s -- install darwin-multi --extra-conf "access-tokens = github.com=${{ secrets.GITHUB_TOKEN }}" + uses: ./ + with: + local-root: install-root/ + logger: pretty + log-directives: harmonic=trace + backtrace: full + github-token: ${{ secrets.GITHUB_TOKEN }} - name: echo $PATH run: echo $PATH - name: Test `nix` with `$GITHUB_PATH` @@ -277,4 +374,9 @@ jobs: shell: fish --login {0} - name: Repeated uninstall run: sudo -E /nix/harmonic uninstall + env: + HARMONIC_NO_CONFIRM: true + HARMONIC_LOGGER: pretty + HARMONIC_LOG_DIRECTIVES: harmonic=trace + RUST_BACKTRACE: full \ No newline at end of file diff --git a/README.md b/README.md index cf35d46..0085040 100644 --- a/README.md +++ b/README.md @@ -173,4 +173,28 @@ Documentation is also available via `nix` build: ```bash nix build github:DeterminateSystems/harmonic#harmonic.doc firefox result-doc/harmonic/index.html +``` + +## As a Github Action + +You can use Harmonic as a Github action like so: + +```yaml +on: + pull_request: + push: + branches: [main] + +jobs: + lints: + name: Build + runs-on: ubuntu-22.04 + steps: + - uses: actions/checkout@v3 + - name: Install Nix + uses: DeterminateSystems/harmonic@main + with: + github-token: ${{ secrets.GITHUB_TOKEN }} + - name: Run `nix build` + run: nix build . ``` \ No newline at end of file diff --git a/action.yml b/action.yml new file mode 100644 index 0000000..ee08eac --- /dev/null +++ b/action.yml @@ -0,0 +1,199 @@ +name: Harmonic +description: Install Nix +inputs: + planner: + description: A planner to use + required: false + extra-args: + description: Extra args to pass to the planner (prefer using structured `with:` arguments unless using a custom planner!) + required: false + github-token: + description: A Github Token so that authenticated requests can be made (set this to `secrets.GITHUB_TOKEN`) + channels: + description: Channel(s) to add (eg `nixpkgs=https://nixos.org/channels/nixpkgs-unstable`) + required: false + modify-profile: + description: Modify the user profile to automatically load nix + required: false + daemon-user-count: + description: Number of build users to create + required: false + nix-build-group-name: + description: The Nix build group name + required: false + nix-build-group-id: + description: The Nix build group GID + required: false + nix-build-user-prefix: + description: The Nix build user prefix (user numbers will be postfixed) + required: false + nix-build-user-base: + description: The Nix build user base UID (ascending) + required: false + nix-package-url: + description: The Nix package URL + required: false + extra-conf: + description: Extra configuration lines for `/etc/nix.conf` (includes `access-tokens` with `secrets.GITHUB_TOKEN` automatically if `github-token` is set) + required: false + mac-encrypt: + description: Force encryption on the volume (Mac only) + required: false + mac-case-sensitive: + description: Use a case sensitive volume (Mac only) + required: false + mac-volume-label: + description: The label for the created APFS volume (Mac only) + required: false + mac-root-disk: + description: The root disk of the target (Mac only) + required: false + nix-install-url: + description: A URL pointing to a harmonic `nix-install.sh` script + required: true + default: https://install.determinate.systems/nix + local-root: + description: A local `harmonic` binary root, overrides the `nix-install` setting (binaries should be named `harmonic-$ARCH`, eg. `harmonic-x86_64-linux`) + required: false + logger: + description: The logger to use for install (eg. `pretty`, `json`, `full`, `compact`) + required: false + log-directives: + description: A list of Tracing directives, comma separated (eg. `harmonic=trace`, see https://docs.rs/tracing-subscriber/latest/tracing_subscriber/filter/struct.EnvFilter.html#directives) + required: false + backtrace: + description: The setting for `RUST_BACKTRACE` (see https://doc.rust-lang.org/std/backtrace/index.html#environment-variables) + required: false + + +runs: + using: composite + steps: + - name: Install Nix + shell: bash + run: | + if [ -n "${{ inputs.channels }}" ]; then + export HARMONIC_CHANNELS=${{ inputs.channels }} + echo "Set HARMONIC_CHANNELS=$HARMONIC_CHANNELS" + fi + + if [ -n "${{ inputs.modify-profile }}" ]; then + export HARMONIC_MODIFY_PROFILE=${{ inputs.modify-profile }} + echo "Set HARMONIC_MODIFY_PROFILE=$HARMONIC_MODIFY_PROFILE" + fi + + if [ -n "${{ inputs.daemon-user-count }}" ]; then + export HARMONIC_DAEMON_USER_COUNT=${{ inputs.daemon-user-count }} + echo "Set HARMONIC_DAEMON_USER_COUNT=$HARMONIC_DAEMON_USER_COUNT" + fi + + if [ -n "${{ inputs.nix-build-group-name }}" ]; then + export HARMONIC_NIX_BUILD_GROUP_NAME=${{ inputs.nix-build-group-name }} + echo "Set HARMONIC_NIX_BUILD_GROUP_NAME=$HARMONIC_NIX_BUILD_GROUP_NAME" + fi + + if [ -n "${{ inputs.nix-build-group-id }}" ]; then + export HARMONIC_NIX_BUILD_GROUP_ID=${{ inputs.nix-build-group-id }} + echo "Set HARMONIC_NIX_BUILD_GROUP_ID=$HARMONIC_NIX_BUILD_GROUP_ID" + fi + + if [ -n "${{ inputs.nix-build-user-prefix }}" ]; then + export HARMONIC_NIX_BUILD_USER_ID_BASE=${{ inputs.nix-build-user-prefix }} + echo "Set HARMONIC_NIX_BUILD_USER_ID_BASE=$HARMONIC_NIX_BUILD_USER_ID_BASE" + fi + + if [ -n "${{ inputs.nix-build-user-base }}" ]; then + export HARMONIC_NIX_BUILD_USER_PREFIX=${{ inputs.nix-build-user-base }} + echo "Set HARMONIC_NIX_BUILD_USER_PREFIX=$HARMONIC_NIX_BUILD_USER_PREFIX" + fi + + if [ -n "${{ inputs.nix-package-url }}" ]; then + export HARMONIC_NIX_PACKAGE_URL=${{ inputs.nix-package-url }} + echo "Set HARMONIC_NIX_PACKAGE_URL=$HARMONIC_NIX_PACKAGE_URL" + fi + + if [ -n "${{ inputs.extra-conf }}" ]; then + if [ -n "${{ inputs.github-token }}" ]; then + export HARMONIC_EXTRA_CONF="${{ inputs.extra-conf }}\naccess-tokens = github.com=${{ inputs.github-token }}" + else + export HARMONIC_EXTRA_CONF="${{ inputs.extra-conf }}" + fi + echo "Set HARMONIC_EXTRA_CONF=$HARMONIC_EXTRA_CONF" + else + if [ -n "${{ inputs.github-token }}" ]; then + export HARMONIC_EXTRA_CONF="access-tokens = github.com=${{ inputs.github-token }}" + echo "Set HARMONIC_EXTRA_CONF=$HARMONIC_EXTRA_CONF" + fi + fi + + if [ -n "${{ inputs.mac-encrypt }}" ]; then + export HARMONIC_ENCRYPT=${{ inputs.mac-encrypt }} + echo "Set HARMONIC_ENCRYPT=$HARMONIC_ENCRYPT" + fi + + if [ -n "${{ inputs.mac-case-sensitive }}" ]; then + export HARMONIC_CASE_SENSITIVE=${{ inputs.mac-case-sensitive }} + echo "Set HARMONIC_CASE_SENSITIVE=$HARMONIC_CASE_SENSITIVE" + fi + + if [ -n "${{ inputs.mac-volume-label }}" ]; then + export HARMONIC_VOLUME_LABEL=${{ inputs.mac-volume-label }} + echo "Set HARMONIC_VOLUME_LABEL=$HARMONIC_VOLUME_LABEL" + fi + + if [ -n "${{ inputs.mac-root-disk }}" ]; then + export HARMONIC_ROOT_DISK=${{ inputs.mac-root-disk }} + echo "Set HARMONIC_ROOT_DISK=$HARMONIC_ROOT_DISK" + fi + + if [ -n "${{ inputs.local-root }}" ]; then + if [ "$RUNNER_OS" == "macOS" ]; then + export PYTHON="python3" + else + export PYTHON="python" + fi + $PYTHON -m http.server --directory ${{ inputs.local-root }} --bind 0.0.0.0 8000 & + export HTTP_PID=$! + echo "Started simple http server for ${{ inputs.local-root }} on 0.0.0.0:8000" + while (! (: /dev/null); do + sleep 1 + done + export NIX_INSTALL_FORCE_ALLOW_HTTP="1" + echo "Set NIX_INSTALL_FORCE_ALLOW_HTTP=$NIX_INSTALL_FORCE_ALLOW_HTTP" + export NIX_INSTALL_URL=0.0.0.0:8000/nix-install.sh + echo "Set NIX_INSTALL_URL=$NIX_INSTALL_URL" + export NIX_INSTALL_BINARY_ROOT=http://0.0.0.0:8000/ + echo "Set NIX_INSTALL_BINARY_ROOT=$NIX_INSTALL_BINARY_ROOT" + export NIX_INSTALL_FORCE_ALLOW_HTTP=1 + echo "Set NIX_INSTALL_FORCE_ALLOW_HTTP=$NIX_INSTALL_FORCE_ALLOW_HTTP" + else + export NIX_INSTALL_URL=${{ inputs.nix-install-url }} + echo "Set NIX_INSTALL_URL=$NIX_INSTALL_URL" + fi + + if [ -n "${{ inputs.logger }}" ]; then + export HARMONIC_LOGGER=${{ inputs.logger }} + echo "Set HARMONIC_LOGGER=$HARMONIC_LOGGER" + fi + + if [ -n "${{ inputs.log-directives }}" ]; then + export HARMONIC_LOG_DIRECTIVES=${{ inputs.log-directives }} + echo "Set HARMONIC_LOG_DIRECTIVES=$HARMONIC_LOG_DIRECTIVES" + fi + + if [ -n "${{ inputs.backtrace }}" ]; then + export RUST_BACKTRACE=${{ inputs.backtrace }} + echo "Set RUST_BACKTRACE=$RUST_BACKTRACE" + fi + + export HARMONIC_NO_CONFIRM=true + echo "Set HARMONIC_NO_CONFIRM=$HARMONIC_NO_CONFIRM" + + curl --retry 20 -L $NIX_INSTALL_URL | sh -s -- install ${{ inputs.planner }} ${{ inputs.extra-args }} + + if [ -n "$HTTP_PID" ]; then + kill $HTTP_PID + fi + + + diff --git a/nix-install.sh b/nix-install.sh index 7265d04..9681a52 100755 --- a/nix-install.sh +++ b/nix-install.sh @@ -21,8 +21,8 @@ fi set -u -# If NIX_INSTALL_UPDATE_ROOT is unset or empty, default it. -NIX_INSTALL_UPDATE_ROOT="${NIX_INSTALL_UPDATE_ROOT:-https://install.determinate.systems/nix}" +# If NIX_INSTALL_BINARY_ROOT is unset or empty, default it. +NIX_INSTALL_BINARY_ROOT="${NIX_INSTALL_BINARY_ROOT:-https://install.determinate.systems/nix}" main() { downloader --check @@ -44,7 +44,7 @@ main() { ;; esac - local _url="${NIX_INSTALL_OVERRIDE_URL-${NIX_INSTALL_UPDATE_ROOT}/harmonic-${_arch}${_ext}}" + local _url="${NIX_INSTALL_OVERRIDE_URL-${NIX_INSTALL_BINARY_ROOT}/harmonic-${_arch}${_ext}}" local _dir if ! _dir="$(ensure mktemp -d)"; then diff --git a/src/action/base/create_directory.rs b/src/action/base/create_directory.rs index dd509a2..8fd56da 100644 --- a/src/action/base/create_directory.rs +++ b/src/action/base/create_directory.rs @@ -4,6 +4,7 @@ use std::path::{Path, PathBuf}; use nix::unistd::{chown, Group, User}; use tokio::fs::{create_dir, remove_dir_all}; +use tracing::{span, Span}; use crate::action::{Action, ActionDescription, ActionState}; use crate::action::{ActionError, StatefulAction}; @@ -74,16 +75,24 @@ impl Action for CreateDirectory { format!("Create directory `{}`", self.path.display()) } + fn tracing_span(&self) -> Span { + span!( + tracing::Level::DEBUG, + "create_directory", + path = tracing::field::display(self.path.display()), + user = self.user, + group = self.group, + mode = self + .mode + .map(|v| tracing::field::display(format!("{:#o}", v))), + ) + } + fn execute_description(&self) -> Vec { vec![ActionDescription::new(self.tracing_synopsis(), vec![])] } - #[tracing::instrument(level = "debug", skip_all, fields( - path = %self.path.display(), - user = self.user, - group = self.group, - mode = self.mode.map(|v| tracing::field::display(format!("{:#o}", v))), - ))] + #[tracing::instrument(level = "debug", skip_all)] async fn execute(&mut self) -> Result<(), ActionError> { let Self { path, @@ -150,12 +159,7 @@ impl Action for CreateDirectory { )] } - #[tracing::instrument(level = "debug", skip_all, fields( - path = %self.path.display(), - user = self.user, - group = self.group, - mode = self.mode.map(|v| tracing::field::display(format!("{:#o}", v))), - ))] + #[tracing::instrument(level = "debug", skip_all)] async fn revert(&mut self) -> Result<(), ActionError> { let Self { path, diff --git a/src/action/base/create_file.rs b/src/action/base/create_file.rs index d474836..6c56337 100644 --- a/src/action/base/create_file.rs +++ b/src/action/base/create_file.rs @@ -1,4 +1,5 @@ use nix::unistd::{chown, Group, User}; +use tracing::{span, Span}; use std::path::{Path, PathBuf}; use tokio::{ @@ -58,16 +59,31 @@ impl Action for CreateFile { fn tracing_synopsis(&self) -> String { format!("Create or overwrite file `{}`", self.path.display()) } + + fn tracing_span(&self) -> Span { + let span = span!( + tracing::Level::DEBUG, + "create_file", + path = tracing::field::display(self.path.display()), + user = self.user, + group = self.group, + mode = self + .mode + .map(|v| tracing::field::display(format!("{:#o}", v))), + buf = tracing::field::Empty, + ); + + if tracing::enabled!(tracing::Level::TRACE) { + span.record("buf", &self.buf); + } + span + } + fn execute_description(&self) -> Vec { vec![ActionDescription::new(self.tracing_synopsis(), vec![])] } - #[tracing::instrument(level = "debug", skip_all, fields( - path = %self.path.display(), - user = self.user, - group = self.group, - mode = self.mode.map(|v| tracing::field::display(format!("{:#o}", v))), - ))] + #[tracing::instrument(level = "debug", skip_all)] async fn execute(&mut self) -> Result<(), ActionError> { let Self { path, @@ -78,6 +94,11 @@ impl Action for CreateFile { force: _, } = self; + if tracing::enabled!(tracing::Level::TRACE) { + let span = tracing::Span::current(); + span.record("buf", &buf); + } + let mut options = OpenOptions::new(); options.create_new(true).write(true).read(true); @@ -135,12 +156,7 @@ impl Action for CreateFile { )] } - #[tracing::instrument(level = "debug", skip_all, fields( - path = %self.path.display(), - user = self.user, - group = self.group, - mode = self.mode.map(|v| tracing::field::display(format!("{:#o}", v))), - ))] + #[tracing::instrument(level = "debug", skip_all)] async fn revert(&mut self) -> Result<(), ActionError> { let Self { path, diff --git a/src/action/base/create_group.rs b/src/action/base/create_group.rs index c3181bb..4f76c94 100644 --- a/src/action/base/create_group.rs +++ b/src/action/base/create_group.rs @@ -1,4 +1,5 @@ use tokio::process::Command; +use tracing::{span, Span}; use crate::action::ActionError; use crate::execute_command; @@ -37,10 +38,16 @@ impl Action for CreateGroup { )] } - #[tracing::instrument(level = "debug", skip_all, fields( - user = self.name, - gid = self.gid, - ))] + fn tracing_span(&self) -> Span { + span!( + tracing::Level::DEBUG, + "create_group", + user = self.name, + gid = self.gid, + ) + } + + #[tracing::instrument(level = "debug", skip_all)] async fn execute(&mut self) -> Result<(), ActionError> { let Self { name, gid } = self; @@ -108,10 +115,7 @@ impl Action for CreateGroup { )] } - #[tracing::instrument(level = "debug", skip_all, fields( - user = self.name, - gid = self.gid, - ))] + #[tracing::instrument(level = "debug", skip_all)] async fn revert(&mut self) -> Result<(), ActionError> { let Self { name, gid: _ } = self; diff --git a/src/action/base/create_or_append_file.rs b/src/action/base/create_or_append_file.rs index 0a1a818..cbf18a2 100644 --- a/src/action/base/create_or_append_file.rs +++ b/src/action/base/create_or_append_file.rs @@ -1,5 +1,6 @@ use nix::unistd::{chown, Group, User}; +use crate::action::{Action, ActionDescription, ActionError, StatefulAction}; use std::{ io::SeekFrom, os::unix::prelude::PermissionsExt, @@ -9,8 +10,7 @@ use tokio::{ fs::{remove_file, OpenOptions}, io::{AsyncReadExt, AsyncSeekExt, AsyncWriteExt}, }; - -use crate::action::{Action, ActionDescription, ActionError, StatefulAction}; +use tracing::{span, Span}; /** Create a file at the given location with the provided `buf`, optionally with an owning user, group, and mode. @@ -58,16 +58,30 @@ impl Action for CreateOrAppendFile { format!("Create or append file `{}`", self.path.display()) } + fn tracing_span(&self) -> Span { + let span = span!( + tracing::Level::DEBUG, + "create_or_append_file", + path = tracing::field::display(self.path.display()), + user = self.user, + group = self.group, + mode = self + .mode + .map(|v| tracing::field::display(format!("{:#o}", v))), + buf = tracing::field::Empty, + ); + + if tracing::enabled!(tracing::Level::TRACE) { + span.record("buf", &self.buf); + } + span + } + fn execute_description(&self) -> Vec { vec![ActionDescription::new(self.tracing_synopsis(), vec![])] } - #[tracing::instrument(level = "debug", skip_all, fields( - path = %self.path.display(), - user = self.user, - group = self.group, - mode = self.mode.map(|v| tracing::field::display(format!("{:#o}", v))), - ))] + #[tracing::instrument(level = "debug", skip_all)] async fn execute(&mut self) -> Result<(), ActionError> { let Self { path, @@ -142,12 +156,7 @@ impl Action for CreateOrAppendFile { )] } - #[tracing::instrument(level = "debug", skip_all, fields( - path = %self.path.display(), - user = self.user, - group = self.group, - mode = self.mode.map(|v| tracing::field::display(format!("{:#o}", v))), - ))] + #[tracing::instrument(level = "debug", skip_all)] async fn revert(&mut self) -> Result<(), ActionError> { let Self { path, diff --git a/src/action/base/create_user.rs b/src/action/base/create_user.rs index 493ba67..07b7e78 100644 --- a/src/action/base/create_user.rs +++ b/src/action/base/create_user.rs @@ -1,4 +1,5 @@ use tokio::process::Command; +use tracing::{span, Span}; use crate::action::ActionError; use crate::execute_command; @@ -38,6 +39,18 @@ impl Action for CreateUser { self.name, self.uid, self.groupname, self.gid ) } + + fn tracing_span(&self) -> Span { + span!( + tracing::Level::DEBUG, + "create_user", + user = self.name, + uid = self.uid, + groupname = self.groupname, + gid = self.gid, + ) + } + fn execute_description(&self) -> Vec { vec![ActionDescription::new( self.tracing_synopsis(), @@ -47,12 +60,7 @@ impl Action for CreateUser { )] } - #[tracing::instrument(level = "debug", skip_all, fields( - user = self.name, - uid = self.uid, - groupname = self.groupname, - gid = self.gid, - ))] + #[tracing::instrument(level = "debug", skip_all)] async fn execute(&mut self) -> Result<(), ActionError> { let Self { name, @@ -231,11 +239,7 @@ impl Action for CreateUser { )] } - #[tracing::instrument(level = "debug", skip_all, fields( - user = self.name, - uid = self.uid, - gid = self.gid, - ))] + #[tracing::instrument(level = "debug", skip_all)] async fn revert(&mut self) -> Result<(), ActionError> { let Self { name, diff --git a/src/action/base/fetch_and_unpack_nix.rs b/src/action/base/fetch_and_unpack_nix.rs index 033e123..6a58f96 100644 --- a/src/action/base/fetch_and_unpack_nix.rs +++ b/src/action/base/fetch_and_unpack_nix.rs @@ -2,6 +2,7 @@ use std::path::PathBuf; use bytes::Buf; use reqwest::Url; +use tracing::{span, Span}; use crate::action::{Action, ActionDescription, ActionError, StatefulAction}; @@ -31,14 +32,20 @@ impl Action for FetchAndUnpackNix { format!("Fetch `{}` to `{}`", self.url, self.dest.display()) } + fn tracing_span(&self) -> Span { + span!( + tracing::Level::DEBUG, + "fetch_and_unpack_nix", + url = tracing::field::display(&self.url), + dest = tracing::field::display(self.dest.display()), + ) + } + fn execute_description(&self) -> Vec { vec![ActionDescription::new(self.tracing_synopsis(), vec![])] } - #[tracing::instrument(level = "debug", skip_all, fields( - url = %self.url, - dest = %self.dest.display(), - ))] + #[tracing::instrument(level = "debug", skip_all)] async fn execute(&mut self) -> Result<(), ActionError> { let Self { url, dest } = self; @@ -66,10 +73,7 @@ impl Action for FetchAndUnpackNix { vec![/* Deliberately empty -- this is a noop */] } - #[tracing::instrument(level = "debug", skip_all, fields( - url = %self.url, - dest = %self.dest.display(), - ))] + #[tracing::instrument(level = "debug", skip_all)] async fn revert(&mut self) -> Result<(), ActionError> { let Self { url: _, dest: _ } = self; diff --git a/src/action/base/move_unpacked_nix.rs b/src/action/base/move_unpacked_nix.rs index 6615ec2..03d4fe1 100644 --- a/src/action/base/move_unpacked_nix.rs +++ b/src/action/base/move_unpacked_nix.rs @@ -1,5 +1,7 @@ use std::path::{Path, PathBuf}; +use tracing::{span, Span}; + use crate::action::{Action, ActionDescription, ActionError, StatefulAction}; const DEST: &str = "/nix/store"; @@ -27,6 +29,15 @@ impl Action for MoveUnpackedNix { "Move the downloaded Nix into `/nix`".to_string() } + fn tracing_span(&self) -> Span { + span!( + tracing::Level::DEBUG, + "mount_unpacked_nix", + src = tracing::field::display(self.src.display()), + dest = DEST, + ) + } + fn execute_description(&self) -> Vec { vec![ActionDescription::new( format!("Move the downloaded Nix into `/nix`"), @@ -37,10 +48,7 @@ impl Action for MoveUnpackedNix { )] } - #[tracing::instrument(level = "debug", skip_all, fields( - src = %self.src.display(), - dest = DEST, - ))] + #[tracing::instrument(level = "debug", skip_all)] async fn execute(&mut self) -> Result<(), ActionError> { let Self { src } = self; @@ -73,10 +81,7 @@ impl Action for MoveUnpackedNix { vec![/* Deliberately empty -- this is a noop */] } - #[tracing::instrument(level = "debug", skip_all, fields( - src = %self.src.display(), - dest = DEST, - ))] + #[tracing::instrument(level = "debug", skip_all)] async fn revert(&mut self) -> Result<(), ActionError> { // Noop Ok(()) diff --git a/src/action/base/setup_default_profile.rs b/src/action/base/setup_default_profile.rs index 2cdb9b7..361cdaf 100644 --- a/src/action/base/setup_default_profile.rs +++ b/src/action/base/setup_default_profile.rs @@ -6,6 +6,7 @@ use crate::{ use glob::glob; use tokio::process::Command; +use tracing::{span, Span}; use crate::action::{Action, ActionDescription}; @@ -31,13 +32,19 @@ impl Action for SetupDefaultProfile { "Setup the default Nix profile".to_string() } + fn tracing_span(&self) -> Span { + span!( + tracing::Level::DEBUG, + "setup_default_profile", + channels = self.channels.join(","), + ) + } + fn execute_description(&self) -> Vec { vec![ActionDescription::new(self.tracing_synopsis(), vec![])] } - #[tracing::instrument(level = "debug", skip_all, fields( - channels = %self.channels.join(","), - ))] + #[tracing::instrument(level = "debug", skip_all)] async fn execute(&mut self) -> Result<(), ActionError> { let Self { channels } = self; @@ -156,9 +163,7 @@ impl Action for SetupDefaultProfile { )] } - #[tracing::instrument(level = "debug", skip_all, fields( - channels = %self.channels.join(","), - ))] + #[tracing::instrument(level = "debug", skip_all)] async fn revert(&mut self) -> Result<(), ActionError> { std::env::remove_var("NIX_SSL_CERT_FILE"); diff --git a/src/action/common/configure_nix.rs b/src/action/common/configure_nix.rs index c28dd2d..7a53488 100644 --- a/src/action/common/configure_nix.rs +++ b/src/action/common/configure_nix.rs @@ -10,6 +10,7 @@ use crate::{ }; use reqwest::Url; +use tracing::{span, Instrument, Span}; /** Configure Nix and start it @@ -68,6 +69,10 @@ impl Action for ConfigureNix { "Configure Nix".to_string() } + fn tracing_span(&self) -> Span { + span!(tracing::Level::DEBUG, "configure_nix",) + } + fn execute_description(&self) -> Vec { let Self { setup_default_profile, @@ -98,17 +103,67 @@ impl Action for ConfigureNix { } = self; if let Some(configure_shell_profile) = configure_shell_profile { + let setup_default_profile_span = tracing::Span::current().clone(); + let ( + place_nix_configuration_span, + place_channel_configuration_span, + configure_shell_profile_span, + ) = ( + setup_default_profile_span.clone(), + setup_default_profile_span.clone(), + setup_default_profile_span.clone(), + ); tokio::try_join!( - async move { setup_default_profile.try_execute().await }, - async move { place_nix_configuration.try_execute().await }, - async move { place_channel_configuration.try_execute().await }, - async move { configure_shell_profile.try_execute().await }, + async move { + setup_default_profile + .try_execute() + .instrument(setup_default_profile_span) + .await + }, + async move { + place_nix_configuration + .try_execute() + .instrument(place_nix_configuration_span) + .await + }, + async move { + place_channel_configuration + .try_execute() + .instrument(place_channel_configuration_span) + .await + }, + async move { + configure_shell_profile + .try_execute() + .instrument(configure_shell_profile_span) + .await + }, )?; } else { + let place_channel_configuration_span = tracing::Span::current().clone(); + let (setup_default_profile_span, place_nix_configuration_span) = ( + place_channel_configuration_span.clone(), + place_channel_configuration_span.clone(), + ); tokio::try_join!( - async move { setup_default_profile.try_execute().await }, - async move { place_nix_configuration.try_execute().await }, - async move { place_channel_configuration.try_execute().await }, + async move { + setup_default_profile + .try_execute() + .instrument(setup_default_profile_span) + .await + }, + async move { + place_nix_configuration + .try_execute() + .instrument(place_nix_configuration_span) + .await + }, + async move { + place_channel_configuration + .try_execute() + .instrument(place_channel_configuration_span) + .await + }, )?; }; configure_nix_daemon_service.try_execute().await?; diff --git a/src/action/common/configure_shell_profile.rs b/src/action/common/configure_shell_profile.rs index 820f77d..f76a4e3 100644 --- a/src/action/common/configure_shell_profile.rs +++ b/src/action/common/configure_shell_profile.rs @@ -4,6 +4,7 @@ use crate::action::{Action, ActionDescription, ActionError, StatefulAction}; use nix::unistd::User; use std::path::{Path, PathBuf}; use tokio::task::JoinSet; +use tracing::{span, Instrument, Span}; // Fish has different syntax than zsh/bash, treat it separate const PROFILE_FISH_SUFFIX: &str = "conf.d/nix.fish"; @@ -141,6 +142,10 @@ impl Action for ConfigureShellProfile { "Configure the shell profiles".to_string() } + fn tracing_span(&self) -> Span { + span!(tracing::Level::DEBUG, "configure_shell_profile",) + } + fn execute_description(&self) -> Vec { vec![ActionDescription::new( self.tracing_synopsis(), @@ -166,8 +171,10 @@ impl Action for ConfigureShellProfile { let span = tracing::Span::current().clone(); let mut create_or_append_file_clone = create_or_append_file.clone(); let _abort_handle = set.spawn(async move { - let _ = span.enter(); - create_or_append_file_clone.try_execute().await?; + create_or_append_file_clone + .try_execute() + .instrument(span) + .await?; Result::<_, ActionError>::Ok((idx, create_or_append_file_clone)) }); } diff --git a/src/action/common/create_nix_tree.rs b/src/action/common/create_nix_tree.rs index 7c8e0e0..5e99fad 100644 --- a/src/action/common/create_nix_tree.rs +++ b/src/action/common/create_nix_tree.rs @@ -1,3 +1,5 @@ +use tracing::{span, Span}; + use crate::action::base::CreateDirectory; use crate::action::{Action, ActionDescription, ActionError, StatefulAction}; @@ -45,6 +47,10 @@ impl Action for CreateNixTree { "Create a directory tree in `/nix`".to_string() } + fn tracing_span(&self) -> Span { + span!(tracing::Level::DEBUG, "create_nix_tree",) + } + fn execute_description(&self) -> Vec { vec![ActionDescription::new( self.tracing_synopsis(), diff --git a/src/action/common/create_users_and_groups.rs b/src/action/common/create_users_and_groups.rs index 213bdbc..5f80497 100644 --- a/src/action/common/create_users_and_groups.rs +++ b/src/action/common/create_users_and_groups.rs @@ -6,6 +6,7 @@ use crate::{ settings::CommonSettings, }; use tokio::task::JoinSet; +use tracing::{span, Instrument, Span}; #[derive(Debug, serde::Deserialize, serde::Serialize, Clone)] pub struct CreateUsersAndGroups { @@ -62,6 +63,18 @@ impl Action for CreateUsersAndGroups { ) } + fn tracing_span(&self) -> Span { + span!( + tracing::Level::DEBUG, + "create_users_and_group", + daemon_user_count = self.daemon_user_count, + nix_build_group_name = self.nix_build_group_name, + nix_build_group_id = self.nix_build_group_id, + nix_build_user_prefix = self.nix_build_user_prefix, + nix_build_user_id_base = self.nix_build_user_id_base, + ) + } + fn execute_description(&self) -> Vec { let Self { daemon_user_count: _, @@ -91,13 +104,7 @@ impl Action for CreateUsersAndGroups { vec![ActionDescription::new(self.tracing_synopsis(), explanation)] } - #[tracing::instrument(level = "debug", skip_all, fields( - daemon_user_count = self.daemon_user_count, - nix_build_group_name = self.nix_build_group_name, - nix_build_group_id = self.nix_build_group_id, - nix_build_user_prefix = self.nix_build_user_prefix, - nix_build_user_id_base = self.nix_build_user_id_base, - ))] + #[tracing::instrument(level = "debug", skip_all)] async fn execute(&mut self) -> Result<(), ActionError> { let Self { create_users, @@ -129,9 +136,10 @@ impl Action for CreateUsersAndGroups { let mut set = JoinSet::new(); let mut errors: Vec> = Vec::new(); for (idx, create_user) in create_users.iter_mut().enumerate() { + let span = tracing::Span::current().clone(); let mut create_user_clone = create_user.clone(); let _abort_handle = set.spawn(async move { - create_user_clone.try_execute().await?; + create_user_clone.try_execute().instrument(span).await?; Result::<_, _>::Ok((idx, create_user_clone)) }); } @@ -188,13 +196,7 @@ impl Action for CreateUsersAndGroups { )] } - #[tracing::instrument(level = "debug", skip_all, fields( - daemon_user_count = self.daemon_user_count, - nix_build_group_name = self.nix_build_group_name, - nix_build_group_id = self.nix_build_group_id, - nix_build_user_prefix = self.nix_build_user_prefix, - nix_build_user_id_base = self.nix_build_user_id_base, - ))] + #[tracing::instrument(level = "debug", skip_all)] async fn revert(&mut self) -> Result<(), ActionError> { let Self { create_users, @@ -210,9 +212,10 @@ impl Action for CreateUsersAndGroups { let mut errors = Vec::default(); for (idx, create_user) in create_users.iter().enumerate() { + let span = tracing::Span::current().clone(); let mut create_user_clone = create_user.clone(); let _abort_handle = set.spawn(async move { - create_user_clone.try_revert().await?; + create_user_clone.try_revert().instrument(span).await?; Result::<_, ActionError>::Ok((idx, create_user_clone)) }); } diff --git a/src/action/common/place_channel_configuration.rs b/src/action/common/place_channel_configuration.rs index 2ae3e07..c8baba1 100644 --- a/src/action/common/place_channel_configuration.rs +++ b/src/action/common/place_channel_configuration.rs @@ -2,6 +2,7 @@ use crate::action::base::CreateFile; use crate::action::ActionError; use crate::action::{Action, ActionDescription, StatefulAction}; use reqwest::Url; +use tracing::{span, Span}; /** Place a channel configuration containing `channels` to the `$ROOT_HOME/.nix-channels` file @@ -54,13 +55,24 @@ impl Action for PlaceChannelConfiguration { ) } + fn tracing_span(&self) -> Span { + span!( + tracing::Level::DEBUG, + "place_channel_configuration", + channels = self + .channels + .iter() + .map(|(c, u)| format!("{c}={u}")) + .collect::>() + .join(", "), + ) + } + fn execute_description(&self) -> Vec { vec![ActionDescription::new(self.tracing_synopsis(), vec![])] } - #[tracing::instrument(level = "debug", skip_all, fields( - channels = self.channels.iter().map(|(c, u)| format!("{c}={u}")).collect::>().join(", "), - ))] + #[tracing::instrument(level = "debug", skip_all)] async fn execute(&mut self) -> Result<(), ActionError> { let Self { create_file, @@ -82,9 +94,7 @@ impl Action for PlaceChannelConfiguration { )] } - #[tracing::instrument(level = "debug", skip_all, fields( - channels = self.channels.iter().map(|(c, u)| format!("{c}={u}")).collect::>().join(", "), - ))] + #[tracing::instrument(level = "debug", skip_all)] async fn revert(&mut self) -> Result<(), ActionError> { let Self { create_file, diff --git a/src/action/common/place_nix_configuration.rs b/src/action/common/place_nix_configuration.rs index 1199162..b391bf7 100644 --- a/src/action/common/place_nix_configuration.rs +++ b/src/action/common/place_nix_configuration.rs @@ -1,3 +1,5 @@ +use tracing::{span, Span}; + use crate::action::base::{CreateDirectory, CreateFile}; use crate::action::{Action, ActionDescription, ActionError, StatefulAction}; @@ -50,6 +52,10 @@ impl Action for PlaceNixConfiguration { format!("Place the Nix configuration in `{NIX_CONF}`") } + fn tracing_span(&self) -> Span { + span!(tracing::Level::DEBUG, "place_nix_configuration",) + } + fn execute_description(&self) -> Vec { vec![ActionDescription::new( self.tracing_synopsis(), diff --git a/src/action/common/provision_nix.rs b/src/action/common/provision_nix.rs index 73090d7..b337b0c 100644 --- a/src/action/common/provision_nix.rs +++ b/src/action/common/provision_nix.rs @@ -1,3 +1,5 @@ +use tracing::{span, Span}; + use super::{CreateNixTree, CreateUsersAndGroups}; use crate::{ action::{ @@ -48,6 +50,10 @@ impl Action for ProvisionNix { "Provision Nix".to_string() } + fn tracing_span(&self) -> Span { + span!(tracing::Level::DEBUG, "provision_nix",) + } + fn execute_description(&self) -> Vec { let Self { fetch_nix, diff --git a/src/action/darwin/bootstrap_apfs_volume.rs b/src/action/darwin/bootstrap_apfs_volume.rs index ebb937d..958b457 100644 --- a/src/action/darwin/bootstrap_apfs_volume.rs +++ b/src/action/darwin/bootstrap_apfs_volume.rs @@ -1,6 +1,7 @@ use std::path::{Path, PathBuf}; use tokio::process::Command; +use tracing::{span, Span}; use crate::action::{ActionError, StatefulAction}; use crate::execute_command; @@ -32,13 +33,19 @@ impl Action for BootstrapApfsVolume { format!("Bootstrap and kickstart `{}`", self.path.display()) } + fn tracing_span(&self) -> Span { + span!( + tracing::Level::DEBUG, + "bootstrap_volume", + path = %self.path.display(), + ) + } + fn execute_description(&self) -> Vec { vec![ActionDescription::new(self.tracing_synopsis(), vec![])] } - #[tracing::instrument(level = "debug", skip_all, fields( - path = %self.path.display(), - ))] + #[tracing::instrument(level = "debug", skip_all)] async fn execute(&mut self) -> Result<(), ActionError> { let Self { path } = self; @@ -70,9 +77,7 @@ impl Action for BootstrapApfsVolume { )] } - #[tracing::instrument(level = "debug", skip_all, fields( - path = %self.path.display(), - ))] + #[tracing::instrument(level = "debug", skip_all)] async fn revert(&mut self) -> Result<(), ActionError> { let Self { path } = self; diff --git a/src/action/darwin/create_apfs_volume.rs b/src/action/darwin/create_apfs_volume.rs index 687c852..45eb1af 100644 --- a/src/action/darwin/create_apfs_volume.rs +++ b/src/action/darwin/create_apfs_volume.rs @@ -1,6 +1,7 @@ use std::path::{Path, PathBuf}; use tokio::process::Command; +use tracing::{span, Span}; use crate::action::{ActionError, StatefulAction}; use crate::execute_command; @@ -41,15 +42,21 @@ impl Action for CreateApfsVolume { ) } + fn tracing_span(&self) -> Span { + span!( + tracing::Level::DEBUG, + "create_volume", + disk = %self.disk.display(), + name = %self.name, + case_sensitive = %self.case_sensitive, + ) + } + fn execute_description(&self) -> Vec { vec![ActionDescription::new(self.tracing_synopsis(), vec![])] } - #[tracing::instrument(level = "debug", skip_all, fields( - disk = %self.disk.display(), - name = %self.name, - case_sensitive = %self.case_sensitive, - ))] + #[tracing::instrument(level = "debug", skip_all)] async fn execute(&mut self) -> Result<(), ActionError> { let Self { disk, @@ -91,11 +98,7 @@ impl Action for CreateApfsVolume { )] } - #[tracing::instrument(level = "debug", skip_all, fields( - disk = %self.disk.display(), - name = %self.name, - case_sensitive = %self.case_sensitive, - ))] + #[tracing::instrument(level = "debug", skip_all)] async fn revert(&mut self) -> Result<(), ActionError> { let Self { disk: _, diff --git a/src/action/darwin/create_nix_volume.rs b/src/action/darwin/create_nix_volume.rs index 0b66142..7f1fc75 100644 --- a/src/action/darwin/create_nix_volume.rs +++ b/src/action/darwin/create_nix_volume.rs @@ -11,6 +11,7 @@ use std::{ time::Duration, }; use tokio::process::Command; +use tracing::{span, Span}; pub const NIX_VOLUME_MOUNTD_DEST: &str = "/Library/LaunchDaemons/org.nixos.darwin-store.plist"; @@ -143,6 +144,15 @@ impl Action for CreateNixVolume { ) } + fn tracing_span(&self) -> Span { + span!( + tracing::Level::DEBUG, + "create_apfs_volume", + disk = tracing::field::display(self.disk.display()), + name = self.name + ) + } + fn execute_description(&self) -> Vec { let Self { disk: _, name: _, .. @@ -150,7 +160,7 @@ impl Action for CreateNixVolume { vec![ActionDescription::new(self.tracing_synopsis(), vec![])] } - #[tracing::instrument(level = "debug", skip_all, fields(destination,))] + #[tracing::instrument(level = "debug", skip_all)] async fn execute(&mut self) -> Result<(), ActionError> { let Self { disk: _, @@ -213,7 +223,7 @@ impl Action for CreateNixVolume { )] } - #[tracing::instrument(level = "debug", skip_all, fields(disk, name))] + #[tracing::instrument(level = "debug", skip_all)] async fn revert(&mut self) -> Result<(), ActionError> { let Self { disk: _, diff --git a/src/action/darwin/create_synthetic_objects.rs b/src/action/darwin/create_synthetic_objects.rs index bd5f31e..061883f 100644 --- a/src/action/darwin/create_synthetic_objects.rs +++ b/src/action/darwin/create_synthetic_objects.rs @@ -1,4 +1,5 @@ use tokio::process::Command; +use tracing::{span, Span}; use crate::execute_command; @@ -22,6 +23,10 @@ impl Action for CreateSyntheticObjects { "Create objects defined in `/etc/synthetic.conf`".to_string() } + fn tracing_span(&self) -> Span { + span!(tracing::Level::DEBUG, "create_synthetic_objects",) + } + fn execute_description(&self) -> Vec { vec![ActionDescription::new( self.tracing_synopsis(), @@ -29,7 +34,7 @@ impl Action for CreateSyntheticObjects { )] } - #[tracing::instrument(level = "debug", skip_all, fields())] + #[tracing::instrument(level = "debug", skip_all)] async fn execute(&mut self) -> Result<(), ActionError> { // Yup we literally call both and ignore the error! Reasoning: https://github.com/NixOS/nix/blob/95331cb9c99151cbd790ceb6ddaf49fc1c0da4b3/scripts/create-darwin-volume.sh#L261 execute_command( @@ -59,7 +64,7 @@ impl Action for CreateSyntheticObjects { )] } - #[tracing::instrument(level = "debug", skip_all, fields())] + #[tracing::instrument(level = "debug", skip_all)] async fn revert(&mut self) -> Result<(), ActionError> { // Yup we literally call both and ignore the error! Reasoning: https://github.com/NixOS/nix/blob/95331cb9c99151cbd790ceb6ddaf49fc1c0da4b3/scripts/create-darwin-volume.sh#L261 execute_command( diff --git a/src/action/darwin/enable_ownership.rs b/src/action/darwin/enable_ownership.rs index 570c168..d719038 100644 --- a/src/action/darwin/enable_ownership.rs +++ b/src/action/darwin/enable_ownership.rs @@ -2,6 +2,7 @@ use std::io::Cursor; use std::path::{Path, PathBuf}; use tokio::process::Command; +use tracing::{span, Span}; use crate::action::{ActionError, StatefulAction}; use crate::execute_command; @@ -34,13 +35,19 @@ impl Action for EnableOwnership { format!("Enable ownership on {}", self.path.display()) } + fn tracing_span(&self) -> Span { + span!( + tracing::Level::DEBUG, + "enable_ownership", + path = %self.path.display(), + ) + } + fn execute_description(&self) -> Vec { vec![ActionDescription::new(self.tracing_synopsis(), vec![])] } - #[tracing::instrument(level = "debug", skip_all, fields( - path = %self.path.display(), - ))] + #[tracing::instrument(level = "debug", skip_all)] async fn execute(&mut self) -> Result<(), ActionError> { let Self { path } = self; @@ -79,9 +86,7 @@ impl Action for EnableOwnership { vec![] } - #[tracing::instrument(level = "debug", skip_all, fields( - path = %self.path.display(), - ))] + #[tracing::instrument(level = "debug", skip_all)] async fn revert(&mut self) -> Result<(), ActionError> { // noop Ok(()) diff --git a/src/action/darwin/encrypt_apfs_volume.rs b/src/action/darwin/encrypt_apfs_volume.rs index a489bcd..27022d7 100644 --- a/src/action/darwin/encrypt_apfs_volume.rs +++ b/src/action/darwin/encrypt_apfs_volume.rs @@ -7,6 +7,7 @@ use crate::{ use rand::Rng; use std::path::{Path, PathBuf}; use tokio::process::Command; +use tracing::{span, Span}; /** Encrypt an APFS volume @@ -43,6 +44,14 @@ impl Action for EncryptApfsVolume { ) } + fn tracing_span(&self) -> Span { + span!( + tracing::Level::DEBUG, + "encrypt_volume", + disk = tracing::field::display(self.disk.display()), + ) + } + fn execute_description(&self) -> Vec { vec![ActionDescription::new(self.tracing_synopsis(), vec![])] } diff --git a/src/action/darwin/kickstart_launchctl_service.rs b/src/action/darwin/kickstart_launchctl_service.rs index 89abce8..852d8d5 100644 --- a/src/action/darwin/kickstart_launchctl_service.rs +++ b/src/action/darwin/kickstart_launchctl_service.rs @@ -1,4 +1,5 @@ use tokio::process::Command; +use tracing::{span, Span}; use crate::action::{ActionError, StatefulAction}; use crate::execute_command; @@ -28,13 +29,19 @@ impl Action for KickstartLaunchctlService { format!("Kickstart the launchctl unit `{unit}`") } + fn tracing_span(&self) -> Span { + span!( + tracing::Level::DEBUG, + "kickstart_launchctl_service", + unit = %self.unit, + ) + } + fn execute_description(&self) -> Vec { vec![ActionDescription::new(self.tracing_synopsis(), vec![])] } - #[tracing::instrument(level = "debug", skip_all, fields( - unit = %self.unit, - ))] + #[tracing::instrument(level = "debug", skip_all)] async fn execute(&mut self) -> Result<(), ActionError> { let Self { unit } = self; @@ -56,9 +63,7 @@ impl Action for KickstartLaunchctlService { vec![] } - #[tracing::instrument(level = "debug", skip_all, fields( - unit = %self.unit, - ))] + #[tracing::instrument(level = "debug", skip_all)] async fn revert(&mut self) -> Result<(), ActionError> { // noop Ok(()) diff --git a/src/action/darwin/unmount_apfs_volume.rs b/src/action/darwin/unmount_apfs_volume.rs index dfb0ad4..e8ba716 100644 --- a/src/action/darwin/unmount_apfs_volume.rs +++ b/src/action/darwin/unmount_apfs_volume.rs @@ -1,6 +1,7 @@ use std::path::{Path, PathBuf}; use tokio::process::Command; +use tracing::{span, Span}; use crate::action::{ActionError, StatefulAction}; use crate::execute_command; @@ -34,14 +35,20 @@ impl Action for UnmountApfsVolume { format!("Unmount the `{}` APFS volume", self.name) } + fn tracing_span(&self) -> Span { + span!( + tracing::Level::DEBUG, + "unmount_volume", + disk = tracing::field::display(self.disk.display()), + name = self.name, + ) + } + fn execute_description(&self) -> Vec { vec![ActionDescription::new(self.tracing_synopsis(), vec![])] } - #[tracing::instrument(level = "debug", skip_all, fields( - disk = %self.disk.display(), - name = %self.name, - ))] + #[tracing::instrument(level = "debug", skip_all)] async fn execute(&mut self) -> Result<(), ActionError> { let Self { disk: _, name } = self; @@ -62,10 +69,7 @@ impl Action for UnmountApfsVolume { vec![ActionDescription::new(self.tracing_synopsis(), vec![])] } - #[tracing::instrument(level = "debug", skip_all, fields( - disk = %self.disk.display(), - name = %self.name, - ))] + #[tracing::instrument(level = "debug", skip_all)] async fn revert(&mut self) -> Result<(), ActionError> { let Self { disk: _, name } = self; diff --git a/src/action/linux/configure_nix_daemon_service.rs b/src/action/linux/configure_nix_daemon_service.rs index 47f156b..edaead6 100644 --- a/src/action/linux/configure_nix_daemon_service.rs +++ b/src/action/linux/configure_nix_daemon_service.rs @@ -3,6 +3,7 @@ use std::path::{Path, PathBuf}; use target_lexicon::OperatingSystem; use tokio::fs::remove_file; use tokio::process::Command; +use tracing::{span, Span}; use crate::action::{ActionError, StatefulAction}; use crate::execute_command; @@ -50,6 +51,11 @@ impl Action for ConfigureNixDaemonService { fn tracing_synopsis(&self) -> String { "Configure Nix daemon related settings with systemd".to_string() } + + fn tracing_span(&self) -> Span { + span!(tracing::Level::DEBUG, "configure_nix_daemon",) + } + fn execute_description(&self) -> Vec { vec![ActionDescription::new( self.tracing_synopsis(), diff --git a/src/action/linux/start_systemd_unit.rs b/src/action/linux/start_systemd_unit.rs index 20ddbb6..9f1bc64 100644 --- a/src/action/linux/start_systemd_unit.rs +++ b/src/action/linux/start_systemd_unit.rs @@ -1,4 +1,5 @@ use tokio::process::Command; +use tracing::{span, Span}; use crate::action::{ActionError, ActionState, StatefulAction}; use crate::execute_command; @@ -32,13 +33,19 @@ impl Action for StartSystemdUnit { format!("Enable (and start) the systemd unit {}", self.unit) } + fn tracing_span(&self) -> Span { + span!( + tracing::Level::DEBUG, + "start_systemd_unit", + unit = %self.unit, + ) + } + fn execute_description(&self) -> Vec { vec![ActionDescription::new(self.tracing_synopsis(), vec![])] } - #[tracing::instrument(level = "debug", skip_all, fields( - unit = %self.unit, - ))] + #[tracing::instrument(level = "debug", skip_all)] async fn execute(&mut self) -> Result<(), ActionError> { let Self { unit, .. } = self; @@ -64,9 +71,7 @@ impl Action for StartSystemdUnit { )] } - #[tracing::instrument(level = "debug", skip_all, fields( - unit = %self.unit, - ))] + #[tracing::instrument(level = "debug", skip_all)] async fn revert(&mut self) -> Result<(), ActionError> { let Self { unit, .. } = self; diff --git a/src/action/mod.rs b/src/action/mod.rs index 34fb41b..1dae188 100644 --- a/src/action/mod.rs +++ b/src/action/mod.rs @@ -45,6 +45,7 @@ A custom [`Action`] can be created then used in a custom [`Planner`](crate::plan ```rust,no_run use std::{error::Error, collections::HashMap}; +use tracing::{Span, span}; use harmonic::{ InstallPlan, settings::{CommonSettings, InstallSettingsError}, @@ -71,13 +72,19 @@ impl Action for MyAction { "My action".to_string() } + fn tracing_span(&self) -> Span { + span!( + tracing::Level::DEBUG, + "my_action", + // Tracing fields here ... + ) + } + fn execute_description(&self) -> Vec { vec![ActionDescription::new(self.tracing_synopsis(), vec![])] } - #[tracing::instrument(level = "debug", skip_all, fields( - // Tracing fields... - ))] + #[tracing::instrument(level = "debug", skip_all)] async fn execute(&mut self) -> Result<(), ActionError> { // Execute steps ... Ok(()) @@ -87,9 +94,7 @@ impl Action for MyAction { vec![ActionDescription::new(self.tracing_synopsis(), vec![])] } - #[tracing::instrument(level = "debug", skip_all, fields( - // Tracing fields... - ))] + #[tracing::instrument(level = "debug", skip_all)] async fn revert(&mut self) -> Result<(), ActionError> { // Revert steps... Ok(()) @@ -159,6 +164,7 @@ mod stateful; pub use stateful::{ActionState, StatefulAction}; use std::error::Error; use tokio::task::JoinError; +use tracing::Span; use crate::error::HasExpectedErrors; @@ -172,6 +178,14 @@ use crate::error::HasExpectedErrors; pub trait Action: Send + Sync + std::fmt::Debug + dyn_clone::DynClone { /// A synopsis of the action for tracing purposes fn tracing_synopsis(&self) -> String; + /// A tracing span suitable for the action + /// + /// It should be [`tracing::Level::DEBUG`] and contain the same name as the [`typetag::serde`] entry. + /// + /// It may contain any fields, and will be attached in the [`StatefulAction::try_execute`] and [`StatefulAction::try_revert`] functions. + /// + /// See [`tracing::Span`] for more details. + fn tracing_span(&self) -> Span; /// A description of what this action would do during execution /// /// If this action calls sub-[`Action`]s, care should be taken to use [`StatefulAction::describe_execute`] on those actions, not [`execute_description`][Action::execute_description]. diff --git a/src/action/stateful.rs b/src/action/stateful.rs index 556977f..dc21f75 100644 --- a/src/action/stateful.rs +++ b/src/action/stateful.rs @@ -1,4 +1,5 @@ use serde::{Deserialize, Serialize}; +use tracing::{Instrument, Span}; use super::{Action, ActionDescription, ActionError}; @@ -26,6 +27,10 @@ impl StatefulAction> { pub fn tracing_synopsis(&self) -> String { self.action.tracing_synopsis() } + /// A tracing span suitable for the action + pub fn tracing_span(&self) -> Span { + self.action.tracing_span() + } /// A description of what this action would do during execution pub fn describe_execute(&self) -> Vec { match self.state { @@ -47,6 +52,7 @@ impl StatefulAction> { /// Perform any execution steps /// /// You should prefer this ([`try_execute`][StatefulAction::try_execute]) over [`execute`][Action::execute] as it handles [`ActionState`] and does tracing + #[tracing::instrument(level = "debug", skip_all)] pub async fn try_execute(&mut self) -> Result<(), ActionError> { match self.state { ActionState::Completed => { @@ -73,6 +79,7 @@ impl StatefulAction> { /// Perform any revert steps /// /// You should prefer this ([`try_revert`][StatefulAction::try_revert]) over [`revert`][Action::revert] as it handles [`ActionState`] and does tracing + #[tracing::instrument(level = "debug", skip_all)] pub async fn try_revert(&mut self) -> Result<(), ActionError> { match self.state { ActionState::Uncompleted => { @@ -106,6 +113,11 @@ where self.action.tracing_synopsis() } + /// A tracing span suitable for the action + pub fn tracing_span(&self) -> Span { + self.action.tracing_span() + } + pub fn inner(&self) -> &A { &self.action } @@ -137,24 +149,34 @@ where /// /// You should prefer this ([`try_execute`][StatefulAction::try_execute]) over [`execute`][Action::execute] as it handles [`ActionState`] and does tracing pub async fn try_execute(&mut self) -> Result<(), ActionError> { + let span = self.action.tracing_span(); match self.state { ActionState::Completed => { tracing::trace!( + parent: &span, "Completed: (Already done) {}", self.action.tracing_synopsis() ); Ok(()) }, ActionState::Skipped => { - tracing::trace!("Skipped: {}", self.action.tracing_synopsis()); + tracing::trace!(parent: &span, "Skipped: {}", self.action.tracing_synopsis()); Ok(()) }, _ => { self.state = ActionState::Progress; - tracing::debug!("Executing: {}", self.action.tracing_synopsis()); - self.action.execute().await?; + tracing::debug!( + parent: &span, + "Executing: {}", + self.action.tracing_synopsis() + ); + self.action.execute().instrument(span.clone()).await?; self.state = ActionState::Completed; - tracing::debug!("Completed: {}", self.action.tracing_synopsis()); + tracing::debug!( + parent: &span, + "Completed: {}", + self.action.tracing_synopsis() + ); Ok(()) }, } @@ -163,23 +185,33 @@ where /// /// You should prefer this ([`try_revert`][StatefulAction::try_revert]) over [`revert`][Action::revert] as it handles [`ActionState`] and does tracing pub async fn try_revert(&mut self) -> Result<(), ActionError> { + let span = self.action.tracing_span(); match self.state { ActionState::Uncompleted => { tracing::trace!( + parent: &span, "Reverted: (Already done) {}", self.action.tracing_synopsis() ); Ok(()) }, ActionState::Skipped => { - tracing::trace!("Skipped: {}", self.action.tracing_synopsis()); + tracing::trace!(parent: &span, "Skipped: {}", self.action.tracing_synopsis()); Ok(()) }, _ => { self.state = ActionState::Progress; - tracing::debug!("Reverting: {}", self.action.tracing_synopsis()); - self.action.revert().await?; - tracing::debug!("Reverted: {}", self.action.tracing_synopsis()); + tracing::debug!( + parent: &span, + "Reverting: {}", + self.action.tracing_synopsis() + ); + self.action.revert().instrument(span.clone()).await?; + tracing::debug!( + parent: &span, + "Reverted: {}", + self.action.tracing_synopsis() + ); self.state = ActionState::Uncompleted; Ok(()) }, diff --git a/src/cli/arg/instrumentation.rs b/src/cli/arg/instrumentation.rs index 635592b..b2d5dd6 100644 --- a/src/cli/arg/instrumentation.rs +++ b/src/cli/arg/instrumentation.rs @@ -2,7 +2,9 @@ use atty::Stream; use eyre::WrapErr; use std::error::Error; use tracing_error::ErrorLayer; -use tracing_subscriber::{layer::SubscriberExt, util::SubscriberInitExt, EnvFilter}; +use tracing_subscriber::{ + filter::Directive, layer::SubscriberExt, util::SubscriberInitExt, EnvFilter, +}; #[derive(Clone, Default, Debug, clap::ValueEnum)] pub enum Logger { @@ -33,6 +35,11 @@ pub struct Instrumentation { /// Which logger to use #[clap(long, env = "HARMONIC_LOGGER", default_value_t = Default::default(), global = true)] pub logger: Logger, + /// Tracing directives + /// + /// See https://docs.rs/tracing-subscriber/latest/tracing_subscriber/filter/struct.EnvFilter.html#directives + #[clap(long = "log-directive", global = true, env = "HARMONIC_LOG_DIRECTIVES", value_delimiter = ',', num_args = 0..)] + pub log_directives: Vec, } impl<'a> Instrumentation { @@ -120,7 +127,7 @@ impl<'a> Instrumentation { } pub fn filter_layer(&self) -> eyre::Result { - let filter_layer = match EnvFilter::try_from_default_env() { + let mut filter_layer = match EnvFilter::try_from_default_env() { Ok(layer) => layer, Err(e) => { // Catch a parse error and report it, ignore a missing env. @@ -134,6 +141,11 @@ impl<'a> Instrumentation { }, }; + for directive in &self.log_directives { + let directive_clone = directive.clone(); + filter_layer = filter_layer.add_directive(directive_clone); + } + Ok(filter_layer) } } diff --git a/src/cli/subcommand/install.rs b/src/cli/subcommand/install.rs index 4ef038f..3282a51 100644 --- a/src/cli/subcommand/install.rs +++ b/src/cli/subcommand/install.rs @@ -9,6 +9,7 @@ use crate::{ error::HasExpectedErrors, plan::RECEIPT_LOCATION, planner::Planner, + settings::CommonSettings, BuiltinPlanner, InstallPlan, }; use clap::{ArgAction, Parser}; @@ -30,6 +31,9 @@ pub struct Install { )] pub no_confirm: bool, + #[clap(flatten)] + pub settings: CommonSettings, + #[clap( long, env = "HARMONIC_EXPLAIN", @@ -47,12 +51,13 @@ pub struct Install { #[async_trait::async_trait] impl CommandExecute for Install { - #[tracing::instrument(level = "debug", skip_all, fields())] + #[tracing::instrument(level = "debug", skip_all)] async fn execute(self) -> eyre::Result { let Self { no_confirm, plan, planner, + settings, explain, } = self; @@ -97,7 +102,7 @@ impl CommandExecute for Install { serde_json::from_str(&install_plan_string)? }, (None, None) => { - let builtin_planner = BuiltinPlanner::default() + let builtin_planner = BuiltinPlanner::from_common_settings(settings) .await .map_err(|e| eyre::eyre!(e))?; let res = builtin_planner.plan().await; diff --git a/src/cli/subcommand/uninstall.rs b/src/cli/subcommand/uninstall.rs index bc8781a..e9cc07a 100644 --- a/src/cli/subcommand/uninstall.rs +++ b/src/cli/subcommand/uninstall.rs @@ -42,7 +42,7 @@ pub struct Uninstall { #[async_trait::async_trait] impl CommandExecute for Uninstall { - #[tracing::instrument(level = "debug", skip_all, fields())] + #[tracing::instrument(level = "debug", skip_all)] async fn execute(self) -> eyre::Result { let Self { no_confirm, diff --git a/src/planner/linux/multi.rs b/src/planner/linux/multi.rs index 2975d41..cc8b652 100644 --- a/src/planner/linux/multi.rs +++ b/src/planner/linux/multi.rs @@ -11,6 +11,7 @@ use crate::{ Action, BuiltinPlanner, }; use std::{collections::HashMap, path::Path}; +use tokio::process::Command; /// A planner for Linux multi-user installs #[derive(Debug, Clone, serde::Serialize, serde::Deserialize)] @@ -37,7 +38,7 @@ impl Planner for LinuxMulti { } // For now, we don't try to repair the user's Nix install or anything special. - if let Ok(_) = tokio::process::Command::new("nix-env") + if let Ok(_) = Command::new("nix-env") .arg("--version") .stdin(std::process::Stdio::null()) .status() diff --git a/src/planner/linux/steam_deck.rs b/src/planner/linux/steam_deck.rs index 4f7c745..6a7bc51 100644 --- a/src/planner/linux/steam_deck.rs +++ b/src/planner/linux/steam_deck.rs @@ -101,6 +101,11 @@ impl Planner for SteamDeck { async fn plan(&self) -> Result>>, PlannerError> { let persistence = &self.persistence; + if !persistence.is_absolute() { + return Err(PlannerError::Custom(Box::new( + SteamDeckError::AbsolutePathRequired(self.persistence.clone()), + ))); + }; let nix_directory_buf = format!( "\ @@ -247,3 +252,9 @@ impl Into for SteamDeck { BuiltinPlanner::SteamDeck(self) } } + +#[derive(thiserror::Error, Debug)] +enum SteamDeckError { + #[error("`{0}` is not a path that can be canonicalized into an absolute path, bind mounts require an absolute path")] + AbsolutePathRequired(PathBuf), +} diff --git a/src/planner/mod.rs b/src/planner/mod.rs index 90dd5fa..ec0310b 100644 --- a/src/planner/mod.rs +++ b/src/planner/mod.rs @@ -82,7 +82,7 @@ use std::collections::HashMap; use crate::{ action::{ActionError, StatefulAction}, error::HasExpectedErrors, - settings::InstallSettingsError, + settings::{CommonSettings, InstallSettingsError}, Action, HarmonicError, InstallPlan, }; @@ -144,6 +144,16 @@ impl BuiltinPlanner { } } + pub async fn from_common_settings(settings: CommonSettings) -> Result { + let mut built = Self::default().await?; + match &mut built { + BuiltinPlanner::LinuxMulti(inner) => inner.settings = settings, + BuiltinPlanner::DarwinMulti(inner) => inner.settings = settings, + BuiltinPlanner::SteamDeck(inner) => inner.settings = settings, + } + Ok(built) + } + pub async fn plan(self) -> Result { match self { BuiltinPlanner::LinuxMulti(planner) => InstallPlan::plan(planner).await, diff --git a/src/settings.rs b/src/settings.rs index e9aafea..118de82 100644 --- a/src/settings.rs +++ b/src/settings.rs @@ -32,14 +32,16 @@ Settings which only apply to certain [`Planner`](crate::planner::Planner)s shoul pub struct CommonSettings { /// Channel(s) to add #[cfg_attr( - feature = "cli",clap( - long, - value_parser, - name = "channel", - action = clap::ArgAction::Append, - env = "HARMONIC_CHANNEL", - default_value = "nixpkgs=https://nixos.org/channels/nixpkgs-unstable", - ))] + feature = "cli", + clap( + long, + value_parser, + name = "channel", + action = clap::ArgAction::Append, + env = "HARMONIC_CHANNELS", + default_value = "nixpkgs=https://nixos.org/channels/nixpkgs-unstable", + ) + )] pub(crate) channels: Vec, /// Modify the user profile to automatically load nix @@ -59,26 +61,44 @@ pub struct CommonSettings { /// Number of build users to create #[cfg_attr( feature = "cli", - clap(long, default_value = "32", env = "HARMONIC_DAEMON_USER_COUNT") + clap( + long, + default_value = "32", + env = "HARMONIC_DAEMON_USER_COUNT", + global = true + ) )] pub(crate) daemon_user_count: usize, /// The Nix build group name #[cfg_attr( feature = "cli", - clap(long, default_value = "nixbld", env = "HARMONIC_NIX_BUILD_GROUP_NAME") + clap( + long, + default_value = "nixbld", + env = "HARMONIC_NIX_BUILD_GROUP_NAME", + global = true + ) )] pub(crate) nix_build_group_name: String, /// The Nix build group GID #[cfg_attr( feature = "cli", - clap(long, default_value_t = 3000, env = "HARMONIC_NIX_BUILD_GROUP_ID") + clap( + long, + default_value_t = 3000, + env = "HARMONIC_NIX_BUILD_GROUP_ID", + global = true + ) )] pub(crate) nix_build_group_id: usize, /// The Nix build user prefix (user numbers will be postfixed) - #[cfg_attr(feature = "cli", clap(long, env = "HARMONIC_NIX_BUILD_USER_PREFIX"))] + #[cfg_attr( + feature = "cli", + clap(long, env = "HARMONIC_NIX_BUILD_USER_PREFIX", global = true) + )] #[cfg_attr( all(target_os = "macos", feature = "cli"), clap(default_value = "_nixbld") @@ -90,7 +110,10 @@ pub struct CommonSettings { pub(crate) nix_build_user_prefix: String, /// The Nix build user base UID (ascending) - #[cfg_attr(feature = "cli", clap(long, env = "HARMONIC_NIX_BUILD_USER_ID_BASE"))] + #[cfg_attr( + feature = "cli", + clap(long, env = "HARMONIC_NIX_BUILD_USER_ID_BASE", global = true) + )] #[cfg_attr(all(target_os = "macos", feature = "cli"), clap(default_value_t = 300))] #[cfg_attr( all(target_os = "linux", feature = "cli"), @@ -99,7 +122,10 @@ pub struct CommonSettings { pub(crate) nix_build_user_id_base: usize, /// The Nix package URL - #[cfg_attr(feature = "cli", clap(long, env = "HARMONIC_NIX_PACKAGE_URL"))] + #[cfg_attr( + feature = "cli", + clap(long, env = "HARMONIC_NIX_PACKAGE_URL", global = true) + )] #[cfg_attr( all(target_os = "macos", target_arch = "x86_64", feature = "cli"), clap( @@ -127,7 +153,7 @@ pub struct CommonSettings { pub(crate) nix_package_url: Url, /// Extra configuration lines for `/etc/nix.conf` - #[cfg_attr(feature = "cli", clap(long, env = "HARMONIC_EXTRA_CONF"))] + #[cfg_attr(feature = "cli", clap(long, action = ArgAction::Set, num_args = 0.., value_delimiter = ',', env = "HARMONIC_EXTRA_CONF", global = true))] pub extra_conf: Vec, /// If Harmonic should forcibly recreate files it finds existing