diff --git a/.devcontainer/Dockerfile b/.devcontainer/Dockerfile new file mode 100644 index 00000000000..f6098460da3 --- /dev/null +++ b/.devcontainer/Dockerfile @@ -0,0 +1,66 @@ +# Update the VARIANT arg in devcontainer.json to pick an Go version +ARG VARIANT=1 +FROM golang:${VARIANT} + +# Options for setup script +ARG INSTALL_ZSH="true" +ARG UPGRADE_PACKAGES="false" +ARG USERNAME=vscode +ARG USER_UID=1000 +ARG USER_GID=$USER_UID + +# Install needed packages and setup non-root user. Use a separate RUN statement to add your own dependencies. +COPY library-scripts/*.sh /tmp/library-scripts/ +RUN apt-get update \ + && /bin/bash /tmp/library-scripts/common-debian.sh "${INSTALL_ZSH}" "${USERNAME}" "${USER_UID}" "${USER_GID}" "${UPGRADE_PACKAGES}" \ + && apt-get autoremove -y && apt-get clean -y && rm -rf /var/lib/apt/lists/* /tmp/library-scripts + +# Install Go tools +ARG GO_TOOLS_WITH_MODULES="\ + golang.org/x/tools/gopls \ + honnef.co/go/tools/... \ + golang.org/x/tools/cmd/gorename \ + golang.org/x/tools/cmd/goimports \ + golang.org/x/tools/cmd/guru \ + golang.org/x/lint/golint \ + github.com/mdempsky/gocode \ + github.com/cweill/gotests/... \ + github.com/haya14busa/goplay/cmd/goplay \ + github.com/sqs/goreturns \ + github.com/josharian/impl \ + github.com/davidrjenni/reftools/cmd/fillstruct \ + github.com/uudashr/gopkgs/v2/cmd/gopkgs \ + github.com/ramya-rao-a/go-outline \ + github.com/acroca/go-symbols \ + github.com/godoctor/godoctor \ + github.com/rogpeppe/godef \ + github.com/zmb3/gogetdoc \ + github.com/fatih/gomodifytags \ + github.com/mgechev/revive \ + github.com/go-delve/delve/cmd/dlv" +RUN mkdir -p /tmp/gotools \ + && cd /tmp/gotools \ + && export GOPATH=/tmp/gotools \ + # Go tools w/module support + && export GO111MODULE=on \ + && (echo "${GO_TOOLS_WITH_MODULES}" | xargs -n 1 go get -x )2>&1 \ + # gocode-gomod + && export GO111MODULE=auto \ + && go get -x -d github.com/stamblerre/gocode 2>&1 \ + && go build -o gocode-gomod github.com/stamblerre/gocode \ + # golangci-lint + && curl -sSfL https://raw.githubusercontent.com/golangci/golangci-lint/master/install.sh | sh -s -- -b /usr/local/bin 2>&1 \ + # Move Go tools into path and clean up + && mv /tmp/gotools/bin/* /usr/local/bin/ \ + && mv gocode-gomod /usr/local/bin/ \ + && rm -rf /tmp/gotools + +ENV GO111MODULE=auto + +# [Optional] Uncomment the next line to use go get to install anything else you need +RUN go get -x mvdan.cc/gofumpt/gofumports + +# [Optional] Uncomment this section to install additional OS packages. +# RUN apt-get update \ +# && export DEBIAN_FRONTEND=noninteractive \ +# && apt-get -y install --no-install-recommends \ No newline at end of file diff --git a/.devcontainer/devcontainer.json b/.devcontainer/devcontainer.json new file mode 100644 index 00000000000..8bdca805b2d --- /dev/null +++ b/.devcontainer/devcontainer.json @@ -0,0 +1,30 @@ +{ + "name": "Go", + "build": { + "dockerfile": "Dockerfile", + // Update the VARIANT arg to pick a version of Go + "args": { "VARIANT": "1" } + }, + "runArgs": [ "--cap-add=SYS_PTRACE", "--security-opt", "seccomp=unconfined" ], + + // Set *default* container specific settings.json values on container create. + "settings": { + "terminal.integrated.shell.linux": "/bin/bash", + "go.useGoProxyToCheckForToolUpdates": false, + "go.gopath": "/go" + }, + + // Add the IDs of extensions you want installed when the container is created. + "extensions": [ + "golang.Go" + ], + + // Use 'forwardPorts' to make a list of ports inside the container available locally. + // "forwardPorts": [], + + // Use 'postCreateCommand' to run commands after the container is created. + // "postCreateCommand": "go version", + + // Uncomment to connect as a non-root user. See https://aka.ms/vscode-remote/containers/non-root. + "remoteUser": "vscode" +} \ No newline at end of file diff --git a/.devcontainer/library-scripts/README.md b/.devcontainer/library-scripts/README.md new file mode 100644 index 00000000000..d06dfd1a95e --- /dev/null +++ b/.devcontainer/library-scripts/README.md @@ -0,0 +1,5 @@ +# Warning: Folder contents may be replaced + +The contents of this folder will be automatically replaced with a file of the same name in the [vscode-dev-containers](https://github.com/microsoft/vscode-dev-containers) repository's [script-library folder](https://github.com/microsoft/vscode-dev-containers/tree/master/script-library) whenever the repository is packaged. + +To retain your edits, move the file to a different location. You may also delete the files if they are not needed. \ No newline at end of file diff --git a/.devcontainer/library-scripts/common-debian.sh b/.devcontainer/library-scripts/common-debian.sh new file mode 100755 index 00000000000..ffef7ba2d43 --- /dev/null +++ b/.devcontainer/library-scripts/common-debian.sh @@ -0,0 +1,123 @@ +#!/usr/bin/env bash +#------------------------------------------------------------------------------------------------------------- +# Copyright (c) Microsoft Corporation. All rights reserved. +# Licensed under the MIT License. See https://go.microsoft.com/fwlink/?linkid=2090316 for license information. +#------------------------------------------------------------------------------------------------------------- + +# Syntax: ./common-debian.sh + +set -e + +INSTALL_ZSH=${1:-"true"} +USERNAME=${2:-"$(awk -v val=1000 -F ":" '$3==val{print $1}' /etc/passwd)"} +USER_UID=${3:-1000} +USER_GID=${4:-1000} +UPGRADE_PACKAGES=${5:-"true"} + +if [ "$(id -u)" -ne 0 ]; then + echo 'Script must be run a root. Use sudo or set "USER root" before running the script.' + exit 1 +fi + +# Treat a user name of "none" as root +if [ "${USERNAME}" = "none" ] || [ "${USERNAME}" = "root" ]; then + USERNAME=root + USER_UID=0 + USER_GID=0 +fi + +# Ensure apt is in non-interactive to avoid prompts +export DEBIAN_FRONTEND=noninteractive + +# Install apt-utils to avoid debconf warning +apt-get -y install --no-install-recommends apt-utils 2> >( grep -v 'debconf: delaying package configuration, since apt-utils is not installed' >&2 ) + +# Get to latest versions of all packages +if [ "${UPGRADE_PACKAGES}" = "true" ]; then + apt-get -y upgrade --no-install-recommends +fi + +# Install common developer tools and dependencies +apt-get -y install --no-install-recommends \ + git \ + openssh-client \ + less \ + iproute2 \ + procps \ + curl \ + wget \ + unzip \ + nano \ + jq \ + lsb-release \ + ca-certificates \ + apt-transport-https \ + dialog \ + gnupg2 \ + libc6 \ + libgcc1 \ + libgssapi-krb5-2 \ + libicu[0-9][0-9] \ + liblttng-ust0 \ + libstdc++6 \ + zlib1g \ + locales + +# Ensure at least the en_US.UTF-8 UTF-8 locale is available. +# Common need for both applications and things like the agnoster ZSH theme. +echo "en_US.UTF-8 UTF-8" >> /etc/locale.gen +locale-gen + +# Install libssl1.1 if available +if [[ ! -z $(apt-cache --names-only search ^libssl1.1$) ]]; then + apt-get -y install --no-install-recommends libssl1.1 +fi + +# Install appropriate version of libssl1.0.x if available +LIBSSL=$(dpkg-query -f '${db:Status-Abbrev}\t${binary:Package}\n' -W 'libssl1\.0\.?' 2>&1 || echo '') +if [ "$(echo "$LIBSSL" | grep -o 'libssl1\.0\.[0-9]:' | uniq | sort | wc -l)" -eq 0 ]; then + if [[ ! -z $(apt-cache --names-only search ^libssl1.0.2$) ]]; then + # Debian 9 + apt-get -y install --no-install-recommends libssl1.0.2 + elif [[ ! -z $(apt-cache --names-only search ^libssl1.0.0$) ]]; then + # Ubuntu 18.04, 16.04, earlier + apt-get -y install --no-install-recommends libssl1.0.0 + fi +fi + +# Create or update a non-root user to match UID/GID - see https://aka.ms/vscode-remote/containers/non-root-user. +if id -u $USERNAME > /dev/null 2>&1; then + # User exists, update if needed + if [ "$USER_GID" != "$(id -G $USERNAME)" ]; then + groupmod --gid $USER_GID $USERNAME + usermod --gid $USER_GID $USERNAME + fi + if [ "$USER_UID" != "$(id -u $USERNAME)" ]; then + usermod --uid $USER_UID $USERNAME + fi +else + # Create user + groupadd --gid $USER_GID $USERNAME + useradd -s /bin/bash --uid $USER_UID --gid $USER_GID -m $USERNAME +fi + +# Add add sudo support for non-root user +apt-get install -y sudo +echo $USERNAME ALL=\(root\) NOPASSWD:ALL > /etc/sudoers.d/$USERNAME +chmod 0440 /etc/sudoers.d/$USERNAME + +# Ensure ~/.local/bin is in the PATH for root and non-root users for bash. (zsh is later) +echo "export PATH=\$PATH:\$HOME/.local/bin" | tee -a /root/.bashrc >> /home/$USERNAME/.bashrc +chown $USER_UID:$USER_GID /home/$USERNAME/.bashrc + +# Optionally install and configure zsh +if [ "$INSTALL_ZSH" = "true" ] && [ ! -d "/root/.oh-my-zsh" ]; then + apt-get install -y zsh + sh -c "$(curl -fsSL https://raw.githubusercontent.com/robbyrussell/oh-my-zsh/master/tools/install.sh)" + echo "export PATH=\$PATH:\$HOME/.local/bin" >> /root/.zshrc + cp -R /root/.oh-my-zsh /home/$USERNAME + cp /root/.zshrc /home/$USERNAME + sed -i -e "s/\/root\/.oh-my-zsh/\/home\/$USERNAME\/.oh-my-zsh/g" /home/$USERNAME/.zshrc + chown -R $USER_UID:$USER_GID /home/$USERNAME/.oh-my-zsh /home/$USERNAME/.zshrc +fi + diff --git a/Makefile b/Makefile index 819d01d3ccb..89ba9565312 100644 --- a/Makefile +++ b/Makefile @@ -53,6 +53,10 @@ test-release: --snapshot \ ${GORELEASER_FLAGS} +.PHONY: update-devcontainer +update-devcontainer: + rm -rf .devcontainer && mkdir .devcontainer && curl -sfL https://github.com/microsoft/vscode-dev-containers/archive/master.tar.gz | tar -xzf - -C .devcontainer --strip-components=4 vscode-dev-containers-master/containers/go/.devcontainer + .PHONY: update-install.sh update-install.sh: # FIXME install.sh is generated by godownloader, but godownloader is diff --git a/cmd/config_test.go b/cmd/config_test.go index 18b946e0df3..cb22ba3f0bf 100644 --- a/cmd/config_test.go +++ b/cmd/config_test.go @@ -137,6 +137,9 @@ func TestValidateKeys(t *testing.T) { func newTestConfig(fs vfs.FS, options ...configOption) *Config { return newConfig(append( []configOption{ + withInitCmdConfig(initCmdConfig{ + clone: true, + }), withTestFS(fs), withTestUser("user"), }, @@ -180,6 +183,12 @@ func withGenericSecretCmdConfig(genericSecretCmdConfig genericSecretCmdConfig) c } } +func withInitCmdConfig(initCmdConfig initCmdConfig) configOption { + return func(c *Config) { + c.init = initCmdConfig + } +} + func withMutator(mutator chezmoi.Mutator) configOption { return func(c *Config) { c.mutator = mutator diff --git a/cmd/docs.gen.go b/cmd/docs.gen.go index 9539e948e55..f024d03b36e 100644 --- a/cmd/docs.gen.go +++ b/cmd/docs.gen.go @@ -610,6 +610,7 @@ func init() { "* [Use scripts to perform actions](#use-scripts-to-perform-actions)\n" + " * [Understand how scripts work](#understand-how-scripts-work)\n" + " * [Install packages with scripts](#install-packages-with-scripts)\n" + + "* [Use chezmoi with GitHub Codespaces, Visual Studio Codespaces, Visual Studio Code Remote - Containers](#use-chezmoi-with-github-codespaces-visual-studio-codespaces-visual-studio-code-remote---containers)\n" + "* [Import archives](#import-archives)\n" + "* [Export archives](#export-archives)\n" + "* [Use a non-git version control system](#use-a-non-git-version-control-system)\n" + @@ -1327,6 +1328,76 @@ func init() { "\n" + "This will install `ripgrep` on both Debian/Ubuntu Linux systems and macOS.\n" + "\n" + + "## Use chezmoi with GitHub Codespaces, Visual Studio Codespaces, Visual Studio Code Remote - Containers\n" + + "\n" + + "The following assumes you are using chezmoi 1.8.4 or later. It does not work\n" + + "with earlier versions of chezmoi.\n" + + "\n" + + "You can use chezmoi to manage your dotfiles in [GitHub Codespaces](https://docs.microsoft.com/en/visualstudio/codespaces/reference/personalizing), [Visual Studio Codespaces](https://docs.microsoft.com/en/visualstudio/codespaces/reference/personalizing), and [Visual Studio Code Remote - Containers](https://code.visualstudio.com/docs/remote/containers#_personalizing-with-dotfile-repositories).\n" + + "\n" + + "The workflow is different to using chezmoi on a new machine, notably:\n" + + "* These systems will automatically clone your `dotfiles` repo to `~/dotfiles`,\n" + + " so there is no need to clone your repo yourself.\n" + + "* The installation script must be non-interactive.\n" + + "* When running in a Codespace, the environment variable `CODESPACES` will be set\n" + + " to `true`. You can read its value with the [`env` template\n" + + " function](http://masterminds.github.io/sprig/os.html).\n" + + "\n" + + "First, if you are using a chezmoi configuration file template, ensure that it is\n" + + "non-interactive when running in codespaces, for example, `.chezmoi.toml.tmpl`\n" + + "might contain:\n" + + "\n" + + "```\n" + + "{{- if (env \"CODESPACES\") -}}\n" + + "[data]\n" + + " codespaces = true\n" + + " email = \"user@company.com\"\n" + + "{{- else -}}\n" + + "{{- $email := promptString \"email\" -}}\n" + + "[data]\n" + + " codespaces = false\n" + + " email = {{ $email }}\n" + + "{{- end }}\n" + + "```\n" + + "\n" + + "This also sets the `codespaces` template variable, so you don't have to repeat\n" + + "`(env \"CODESPACES\")` in your templates.\n" + + "\n" + + "Second, create an `install.sh` script that installs chezmoi and your dotfiles:\n" + + "\n" + + "```sh\n" + + "#!/bin/sh\n" + + "\n" + + "if [ \"$CODESPACES\" == \"true\" ] ; then\n" + + " curl -sfL https://git.io/chezmoi | sh\n" + + " ./bin/chezmoi init --apply --clone=false --source=$HOME/dotfiles\n" + + "fi\n" + + "```\n" + + "\n" + + "Ensure that this file is executable (`chmod a+x install.sh`), and add\n" + + "`install.sh` to your `.chezmoiignore` file.\n" + + "\n" + + "Inside the `if` statement, `curl ... | sh` installs the latest version of\n" + + "chezmoi in `./bin` and then `./bin/chezmoi init ...` invokes chezmoi to create\n" + + "its configuration file and initialize your dotfiles. `--apply` tells chezmoi to\n" + + "apply the changes immediately, `--clone=false` tells chezmoi not to clone your\n" + + "dotfiles repo (because this has already been done), and `--source=...` tells\n" + + "chezmoi where to find the cloned `dotfiles` repo.\n" + + "\n" + + "If you do not use a chezmoi configuration file template you can use `chezmoi\n" + + "apply --source=$HOME/dotfiles` instead of `chezmoi init ...` in `install.sh`.\n" + + "\n" + + "Finally, modify any of your templates to use the `codespaces` variable if\n" + + "needed. For example, to install `vim-gtk` on Linux but not in Codespaces, your\n" + + "`run_once_install-packages.sh.tmpl` might contain:\n" + + "\n" + + "```\n" + + "{{- if (and (eq .chezmoi.os \"linux\")) (not .codespaces))) -}}\n" + + "#!/bin/sh\n" + + "sudo apt install -y vim-gtk\n" + + "{{- end -}}\n" + + "```\n" + + "\n" + "## Import archives\n" + "\n" + "It is occasionally useful to import entire archives of configuration into your\n" + @@ -2299,6 +2370,16 @@ func init() { "file is created using that file as a template. Finally, if the `--apply` flag is\n" + "passed, `chezmoi apply` is run.\n" + "\n" + + "#### `--apply`\n" + + "\n" + + "Run `chezmoi apply` after checking out the repo and creating the config file.\n" + + "This is `false` by default.\n" + + "\n" + + "#### `--clone`\n" + + "\n" + + "Create or clone the repo. This is the default. Specify `--clone=false` to skip,\n" + + "for example if the source directory is already checked out.\n" + + "\n" + "#### `init` examples\n" + "\n" + " chezmoi init https://github.com/user/dotfiles.git\n" + diff --git a/cmd/helps.gen.go b/cmd/helps.gen.go index b21a30f6854..7ec7c50126c 100644 --- a/cmd/helps.gen.go +++ b/cmd/helps.gen.go @@ -342,7 +342,17 @@ var helps = map[string]help{ " If a file called `.chezmoi.format.tmpl` exists, where `format` is one of the\n" + " supported file formats (e.g. `json`, `toml`, or `yaml`) then a new\n" + " configuration file is created using that file as a template. Finally, if the `--\n" + - " apply` flag is passed, `chezmoi apply` is run.", + " apply` flag is passed, `chezmoi apply` is run.\n" + + "\n" + + " `--apply`\n" + + "\n" + + " Run `chezmoi apply` after checking out the repo and creating the config file.\n" + + " This is `false` by default.\n" + + "\n" + + " `--clone`\n" + + "\n" + + " Create or clone the repo. This is the default. Specify `--clone=false` to skip,\n" + + " for example if the source directory is already checked out.", example: "" + " chezmoi init https://github.com/user/dotfiles.git\n" + " chezmoi init https://github.com/user/dotfiles.git --apply", diff --git a/cmd/init.go b/cmd/init.go index bf128d6cb79..3d398436a7d 100644 --- a/cmd/init.go +++ b/cmd/init.go @@ -28,6 +28,7 @@ var initCmd = &cobra.Command{ type initCmdConfig struct { apply bool + clone bool } func init() { @@ -35,6 +36,7 @@ func init() { persistentFlags := initCmd.PersistentFlags() persistentFlags.BoolVar(&config.init.apply, "apply", false, "update destination directory") + persistentFlags.BoolVar(&config.init.clone, "clone", true, "clone repo") } func (c *Config) runInitCmd(cmd *cobra.Command, args []string) error { @@ -52,41 +54,43 @@ func (c *Config) runInitCmd(cmd *cobra.Command, args []string) error { return err } - switch len(args) { - case 0: // init - var initArgs []string - if c.SourceVCS.Init != nil { - switch v := c.SourceVCS.Init.(type) { - case string: - initArgs = strings.Split(v, " ") - case []string: - initArgs = v - default: - return fmt.Errorf("sourceVCS.init: cannot parse value") + if c.init.clone { + switch len(args) { + case 0: // init + var initArgs []string + if c.SourceVCS.Init != nil { + switch v := c.SourceVCS.Init.(type) { + case string: + initArgs = strings.Split(v, " ") + case []string: + initArgs = v + default: + return fmt.Errorf("sourceVCS.init: cannot parse value") + } + } else { + initArgs = vcs.InitArgs() } - } else { - initArgs = vcs.InitArgs() - } - if err := c.run(c.SourceDir, c.SourceVCS.Command, initArgs...); err != nil { - return err - } - case 1: // clone - cloneArgs := vcs.CloneArgs(args[0], rawSourceDir) - if cloneArgs == nil { - return fmt.Errorf("%s: cloning not supported", c.SourceVCS.Command) - } - if err := c.run("", c.SourceVCS.Command, cloneArgs...); err != nil { - return err - } - // FIXME this should be part of VCS - if filepath.Base(c.SourceVCS.Command) == "git" { - if _, err := c.fs.Stat(filepath.Join(c.SourceDir, ".gitmodules")); err == nil { - for _, args := range [][]string{ - {"submodule", "init"}, - {"submodule", "update"}, - } { - if err := c.run(c.SourceDir, c.SourceVCS.Command, args...); err != nil { - return err + if err := c.run(c.SourceDir, c.SourceVCS.Command, initArgs...); err != nil { + return err + } + case 1: // clone + cloneArgs := vcs.CloneArgs(args[0], rawSourceDir) + if cloneArgs == nil { + return fmt.Errorf("%s: cloning not supported", c.SourceVCS.Command) + } + if err := c.run("", c.SourceVCS.Command, cloneArgs...); err != nil { + return err + } + // FIXME this should be part of VCS + if filepath.Base(c.SourceVCS.Command) == "git" { + if _, err := c.fs.Stat(filepath.Join(c.SourceDir, ".gitmodules")); err == nil { + for _, args := range [][]string{ + {"submodule", "init"}, + {"submodule", "update"}, + } { + if err := c.run(c.SourceDir, c.SourceVCS.Command, args...); err != nil { + return err + } } } } diff --git a/completions/chezmoi-completion.bash b/completions/chezmoi-completion.bash index 1af62670f10..7c90ba72e60 100644 --- a/completions/chezmoi-completion.bash +++ b/completions/chezmoi-completion.bash @@ -1202,6 +1202,7 @@ _chezmoi_init() flags_completion=() flags+=("--apply") + flags+=("--clone") flags+=("--color=") two_word_flags+=("--color") flags+=("--config=") diff --git a/completions/chezmoi.zsh b/completions/chezmoi.zsh index ad6e5ce6e75..bb093e8597e 100644 --- a/completions/chezmoi.zsh +++ b/completions/chezmoi.zsh @@ -512,6 +512,7 @@ function _chezmoi_import { function _chezmoi_init { _arguments \ '--apply[update destination directory]' \ + '--clone[clone repo]' \ '--color[colorize diffs]:' \ '(-c --config)'{-c,--config}'[config file]:filename:_files' \ '--debug[write debug logs]' \ diff --git a/docs/HOWTO.md b/docs/HOWTO.md index fb5c5c1311c..f76b6db8c7d 100644 --- a/docs/HOWTO.md +++ b/docs/HOWTO.md @@ -27,6 +27,7 @@ * [Use scripts to perform actions](#use-scripts-to-perform-actions) * [Understand how scripts work](#understand-how-scripts-work) * [Install packages with scripts](#install-packages-with-scripts) +* [Use chezmoi with GitHub Codespaces, Visual Studio Codespaces, Visual Studio Code Remote - Containers](#use-chezmoi-with-github-codespaces-visual-studio-codespaces-visual-studio-code-remote---containers) * [Import archives](#import-archives) * [Export archives](#export-archives) * [Use a non-git version control system](#use-a-non-git-version-control-system) @@ -744,6 +745,76 @@ This script can also be a template. For example, if you create This will install `ripgrep` on both Debian/Ubuntu Linux systems and macOS. +## Use chezmoi with GitHub Codespaces, Visual Studio Codespaces, Visual Studio Code Remote - Containers + +The following assumes you are using chezmoi 1.8.4 or later. It does not work +with earlier versions of chezmoi. + +You can use chezmoi to manage your dotfiles in [GitHub Codespaces](https://docs.microsoft.com/en/visualstudio/codespaces/reference/personalizing), [Visual Studio Codespaces](https://docs.microsoft.com/en/visualstudio/codespaces/reference/personalizing), and [Visual Studio Code Remote - Containers](https://code.visualstudio.com/docs/remote/containers#_personalizing-with-dotfile-repositories). + +The workflow is different to using chezmoi on a new machine, notably: +* These systems will automatically clone your `dotfiles` repo to `~/dotfiles`, + so there is no need to clone your repo yourself. +* The installation script must be non-interactive. +* When running in a Codespace, the environment variable `CODESPACES` will be set + to `true`. You can read its value with the [`env` template + function](http://masterminds.github.io/sprig/os.html). + +First, if you are using a chezmoi configuration file template, ensure that it is +non-interactive when running in codespaces, for example, `.chezmoi.toml.tmpl` +might contain: + +``` +{{- if (env "CODESPACES") -}} +[data] + codespaces = true + email = "user@company.com" +{{- else -}} +{{- $email := promptString "email" -}} +[data] + codespaces = false + email = {{ $email }} +{{- end }} +``` + +This also sets the `codespaces` template variable, so you don't have to repeat +`(env "CODESPACES")` in your templates. + +Second, create an `install.sh` script that installs chezmoi and your dotfiles: + +```sh +#!/bin/sh + +if [ "$CODESPACES" == "true" ] ; then + curl -sfL https://git.io/chezmoi | sh + ./bin/chezmoi init --apply --clone=false --source=$HOME/dotfiles +fi +``` + +Ensure that this file is executable (`chmod a+x install.sh`), and add +`install.sh` to your `.chezmoiignore` file. + +Inside the `if` statement, `curl ... | sh` installs the latest version of +chezmoi in `./bin` and then `./bin/chezmoi init ...` invokes chezmoi to create +its configuration file and initialize your dotfiles. `--apply` tells chezmoi to +apply the changes immediately, `--clone=false` tells chezmoi not to clone your +dotfiles repo (because this has already been done), and `--source=...` tells +chezmoi where to find the cloned `dotfiles` repo. + +If you do not use a chezmoi configuration file template you can use `chezmoi +apply --source=$HOME/dotfiles` instead of `chezmoi init ...` in `install.sh`. + +Finally, modify any of your templates to use the `codespaces` variable if +needed. For example, to install `vim-gtk` on Linux but not in Codespaces, your +`run_once_install-packages.sh.tmpl` might contain: + +``` +{{- if (and (eq .chezmoi.os "linux")) (not .codespaces))) -}} +#!/bin/sh +sudo apt install -y vim-gtk +{{- end -}} +``` + ## Import archives It is occasionally useful to import entire archives of configuration into your diff --git a/docs/REFERENCE.md b/docs/REFERENCE.md index 8b7027f87ef..c22b197864f 100644 --- a/docs/REFERENCE.md +++ b/docs/REFERENCE.md @@ -694,6 +694,16 @@ supported file formats (e.g. `json`, `toml`, or `yaml`) then a new configuration file is created using that file as a template. Finally, if the `--apply` flag is passed, `chezmoi apply` is run. +#### `--apply` + +Run `chezmoi apply` after checking out the repo and creating the config file. +This is `false` by default. + +#### `--clone` + +Create or clone the repo. This is the default. Specify `--clone=false` to skip, +for example if the source directory is already checked out. + #### `init` examples chezmoi init https://github.com/user/dotfiles.git diff --git a/main_test.go b/main_test.go index 28263439d14..fee90af9c31 100644 --- a/main_test.go +++ b/main_test.go @@ -72,6 +72,8 @@ func chHome(ts *testscript.TestScript, neg bool, args []string) { if runtime.GOOS == "windows" { ts.Setenv("USERPROFILE", homeDir) } + ts.Setenv("CHEZMOICONFIGDIR", filepath.Join(homeDir, ".config", "chezmoi")) + ts.Setenv("CHEZMOISOURCEDIR", filepath.Join(homeDir, ".local", "share", "chezmoi")) } // edit edits all of its arguments by appending "# edited\n" to them. diff --git a/testdata/scripts/init.txt b/testdata/scripts/init.txt index bdd5d6e40e8..3c2db9eb289 100644 --- a/testdata/scripts/init.txt +++ b/testdata/scripts/init.txt @@ -22,5 +22,14 @@ chezmoi init --apply file://$WORK/home/user/.local/share/chezmoi exists ${CHEZMOISOURCEDIR}${/}.git grep '# contents of .bashrc' $HOME${/}.bashrc +# test that chezmoi init --clone=false does not clone the repo but does create the config file +chhome home4${/}user +chezmoi init --clone=false --source=${HOME}/dotfiles --verbose +! exists ${CHEZMOISOURCEDIR}${/}.git +exists ${CHEZMOICONFIGDIR}/chezmoi.toml + -- home/user/.bashrc -- # contents of .bashrc +-- home4/user/dotfiles/.chezmoi.toml.tmpl -- +[data] + email = "user@home.org"