Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Remote install dotfiles over ssh without installing chezmoi on the machine #1410

Open
twpayne opened this issue Sep 14, 2021 · 5 comments
Open
Labels
enhancement New feature or request v3 Planned for v3

Comments

@twpayne
Copy link
Owner

twpayne commented Sep 14, 2021

Is your feature request related to a problem? Please describe.

It would be nice if chezmoi could install my dotfiles on a machine that I can ssh into, without me having to install chezmoi on that machine. This would be very helpful for ephemeral virtual machines and dev containers, for example.

Describe the solution you'd like

I would like to able to run:

$ chezmoi apply --remote=machine.example.com

and have chezmoi install my dotfiles over SSH on machine.example.com without me having to install chezmoi on machine.example.com.

Describe alternatives you've considered

chezmoi already has much of the functionality to make this possible, it can roughly be simulated with:

$ chezmoi archive | ssh machine.example.com -- tar -xf -

However, chezmoi archive will generate dotfiles for the local machine, not the remote machine. This can be mitigated with

$ chezmoi --config=remote-chezmoi-config.toml archive | ssh machine.example.com -- tar -xf -

where remote-chezmoi-config.toml is a chezmoi configuration file for the remote machine. However, the use of any non-hermetic template variables, e.g. .chezmoi, or the use of non-hermetic template functions, e.g. env, output, or lookPath will still apply to the local machine, not the remote machine.

Possible ways around this include:

  • Requiring the user's templates to not use any non-hermetic template variables or functions. This is possible now.
  • Using a two-phase SSH: in the first phase a SSH connection is used to retrieve the value of .chezmoi from the remote machine, and in the second phase the archive is generated using the value of .chezmoi and extracted on the remote machine. Non-hermetic template functions are not likely to be supported.

Additional context

  • The remote install functionality should also work for local Docker containers.
  • One-shot installation of dotfiles is probably sufficient. There is no need to support diff for remotes.
@twpayne twpayne added the enhancement New feature or request label Sep 14, 2021
@felipecrs
Copy link
Contributor

Another way around it:

ssh -t user@remote.machine 'sh <(curl -fsSL git.io/chezmoi) -- init --one-shot felipecrs'

The caveats are of course:

  1. chezmoi binary needs to be re-downloaded
  2. dotfiles repo needs to be re-downloaded

@twpayne twpayne added the patience Patience required, there is no date for this being fixed label Jan 16, 2022
@twpayne
Copy link
Owner Author

twpayne commented Jun 12, 2022

Random drive-by thoughts:

  • go-git supports in memory storage so it should be possible to do this without touching disk.
  • Rather than creating an archive to be untar'd, the remote install should probably create a portable shell script to be run on the target machine, much like the old shar format.

@twpayne twpayne added v3 Planned for v3 and removed patience Patience required, there is no date for this being fixed labels Jan 2, 2023
@kazhuravlev
Copy link

I believe it will be better if we can directly connect to a remote machine and execute the process remotely. It's not that hard, but it allows us to run hook scripts in a specific environment, and we can also use the local environment to gather additional information. An additional feature - we will not do unnecessary manual work, such as unpacking the archive and then copying the files.

But we also need to think about what we will do after applying the changes:

  • automatically install chezmoi on the remote server (because we might want to manage dotfiles on the remote machine)
  • manage remote dotfiles from the local machine only (without installing chezmoi on a remote machine)
  • hybrid system? in this case we can imagine that we have 10/100 machines. And what do we expect?

If you have answers for this questions and plan for how to implement this feature - I can implement this feature.

@infused-kim
Copy link

This is a great idea!

In the meantime I extended your install script to allow installations over SSH.

When you run install_chezmoi local, it will install it locally in almost the same way your original script did it. But if you add a SSH host as the first parameter like install_chezmoi foo@somedomain.com, then it will first scp itself to the remote host and then execute the script with the local parameter on it.

It works well for me.

#!/bin/sh

set -e # -e: exit on error

# Default repo
repo_default="git@github.com:your_user/dotfiles.git"

print_help() {
  echo "Usage: $0 <destination> [--repo <git-repo>]"
  echo
  echo "This script installs chezmoi either locally or on a remote host."
  echo
  echo "Arguments:"
  echo "  destination  If 'local', the script will install chezmoi locally. If anything else, it will"
  echo "               assume it's an SSH host and will install chezmoi on the remote host."
  echo
  echo "Options:"
  echo "  --repo       Specify a different git repository to use. Default is \"$repo_default\"."
  echo "  -h, --help   Show this help message."
}

install_local() {
  repo="$1"
  if [ ! "$(command -v chezmoi)" ]; then
    bin_dir="$HOME/.local/bin"
    chezmoi="$bin_dir/chezmoi"
    if [ "$(command -v curl)" ]; then
      sh -c "$(curl -fsSL https://git.io/chezmoi)" -- -b "$bin_dir"
    elif [ "$(command -v wget)" ]; then
      sh -c "$(wget -qO- https://git.io/chezmoi)" -- -b "$bin_dir"
    else
      echo "To install chezmoi, you must have curl or wget installed." >&2
      exit 1
    fi
  else
    chezmoi=chezmoi
  fi

  # exec: replace current process with chezmoi init
  exec "$chezmoi" init "$repo"
}

install_remote() {
  repo="$1"
  remote_host="$2"
  script_name="$(basename "$0")"
  scp "$0" "$remote_host:/tmp/$script_name"
  ssh -t "$remote_host" "chmod +x /tmp/$script_name; /tmp/$script_name local --repo $repo"
}

# Parse command-line options
repo="$repo_default"
while [ $# -gt 0 ]; do
  case "$1" in
    --repo)
      shift
      repo="$1"
      ;;
    -h|--help)
      print_help
      exit 0
      ;;
    *)
      # If it's neither --repo nor -h/--help, it must be the destination
      destination="$1"
      ;;
  esac
  shift
done

# If no destination was provided, print the help message
if [ -z "$destination" ]; then
  print_help
  exit 1
fi

if [ "$destination" = "local" ]; then
  install_local "$repo"
else
  install_remote "$repo" "$destination"
fi

@stinovlas
Copy link

I'd be interested in this functionality. As far as simple file transfer is concerned, we already have a working framework for that. However, there are a few things that would need to be handled carefully:

  • running scripts; these should be run at the remote machine (this includes updating tmux / vim plugins or other maintenance tasks)
    • it may be useful to have an option to mark a script as local only or remote only
  • password manager secrets should be read on the local machine (so we don't have to add and authorize the password manager CLI on the remote)

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
enhancement New feature or request v3 Planned for v3
Projects
None yet
Development

No branches or pull requests

5 participants